%endif
Name: crosswalk
-Version: 9.38.207.0
+Version: 10.38.208.0
Release: 0
Summary: Chromium-based app runtime
License: (BSD-3-Clause and LGPL-2.1+)
BuildRequires: pkgconfig(nss)
BuildRequires: pkgconfig(sensor)
BuildRequires: pkgconfig(vconf)
+BuildRequires: pkgconfig(xmlsec1)
%if %{with x}
BuildRequires: pkgconfig(x11)
BuildRequires: pkgconfig(xcomposite)
%else
BuildRequires: pkgconfig(scim)
%endif
+Requires: ca-certificates-tizen
%description
Crosswalk is an app runtime based on Chromium. It is an open source project started by the Intel Open Source Technology Center (http://www.01.org).
'eyes-free':
'http://eyes-free.googlecode.com/svn',
'webkit_rev':
- '@435519d7eb441a56c058e5e98fbd363f3f3aed30',
+ '@43aff830b25c0894823834d6ac417112fefb1fb2',
'blink':
'http://src.chromium.org/blink',
'skia':
'src/chrome/browser/resources/pdf/html_office':
Var('git_url') + '/chromium/html-office-public.git@eeff97614f65e0578529490d44d412032c3d7359',
'src/chrome/test/data/extensions/api_test/permissions/nacl_enabled/bin':
- Var('git_url') + '/native_client/src/native_client/tests/prebuilt.git@3e17365176c94624f46cace174f61834b7f3c35d',
+ Var('git_url') + '/native_client/src/native_client/tests/prebuilt.git@e65f794ce4a809d92d5cacae55ae119946bdb9c5',
'src/chrome/test/data/perf/canvas_bench':
Var('git_url') + '/chromium/canvas_bench.git@a7b40ea5ae0239517d78845a5fc9b12976bfc732',
'src/chrome/test/data/perf/frame_rate/content':
'src/media/cdm/ppapi/api':
Var('git_url') + '/chromium/cdm.git@41c8183a3966a17b440dbe606cb2840e1b7ce884',
'src/native_client':
- Var('git_url') + '/native_client/src/native_client.git@4a2be76975082aad3f769115f23661acca318949',
+ Var('git_url') + '/native_client/src/native_client.git@792e992d22507e6ff10e5088f7f2a1b61e3742a2',
'src/sdch/open-vcdiff':
Var('git_url') + '/external/open-vcdiff.git@438f2a5be6d809bc21611a94cd37bfc8c28ceb33',
'src/testing/gmock':
'src/third_party/colorama/src':
Var('git_url') + '/external/colorama.git@799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8',
'src/third_party/ffmpeg':
- Var('git_url') + '/chromium/third_party/ffmpeg.git@5961d8ac36f995dc3162aff7f8039ef7d772aaac',
+ Var('git_url') + '/chromium/third_party/ffmpeg.git@98ca32e50f6e38447bc81705d0689ebceb6ac649',
'src/third_party/flac':
Var('git_url') + '/chromium/deps/flac.git@0635a091379d9677f1ddde5f2eec85d0f096f219',
'src/third_party/hunspell':
'src/third_party/libjingle/source/talk':
Var('git_url') + '/external/webrtc/trunk/talk.git@3541181607ffe656d8db759a6095864d9f72248d',
'src/third_party/libjpeg_turbo':
- Var('git_url') + '/chromium/deps/libjpeg_turbo.git@841fff8cddd73c0d6b966902f83bea7ad366bd4b',
+ Var('git_url') + '/chromium/deps/libjpeg_turbo.git@2ed5319ce40b0ba2cd9b962713ea0ef775781e69',
'src/third_party/libphonenumber/src/phonenumbers':
Var('git_url') + '/external/libphonenumber/cpp/src/phonenumbers.git@8d8b5b3b2035197795d27573d4cf566b5d9ad689',
'src/third_party/libphonenumber/src/resources':
'src/third_party/libsrtp':
Var('git_url') + '/chromium/deps/libsrtp.git@662d81de5cbf3667f1baaafe051b8b4ab0b12fe2',
'src/third_party/libvpx':
- Var('git_url') + '/chromium/deps/libvpx.git@6e18fa2139dd88a8a5c529e3e6d0fe6d6f76ad1c',
+ Var('git_url') + '/chromium/deps/libvpx.git@8110782824f708dd3630cd55375019648567c31c',
'src/third_party/libwebm/source':
Var('git_url') + '/webm/libwebm.git@0d4cb404ea4195e5e21d04db2c955615535ce62e',
'src/third_party/libyuv':
'src/third_party/sfntly/cpp/src':
Var('git_url') + '/external/sfntly/cpp/src.git@1bdaae8fc788a5ac8936d68bf24f37d977a13dac',
'src/third_party/skia':
- Var('git_url') + '/skia.git@1313b3f69370915a4fd911f97b4975d90bdf8869',
+ Var('git_url') + '/skia.git@773a327b12f653c3bb0630fee1b3ba9629f5717e',
'src/third_party/smhasher/src':
Var('git_url') + '/external/smhasher.git@e87738e57558e0ec472b2fc3a643b838e5b6e88f',
'src/third_party/snappy/src':
'src/third_party/webpagereplay':
Var('git_url') + '/external/web-page-replay.git@b62c02d3b64cf00a2f65a82cca0721aa42c3d6ad',
'src/third_party/webrtc':
- Var('git_url') + '/external/webrtc/trunk/webrtc.git@6c2b7b2432cb42cbef1b32d9ea21b3e9f33b0cda',
+ Var('git_url') + '/external/webrtc/trunk/webrtc.git@a6ed0dfa13f606d7f173f740510c2ed5855aa7fd',
'src/third_party/yasm/source/patched-yasm':
Var('git_url') + '/chromium/deps/yasm/patched-yasm.git@c960eb11ccda80b10ed50be39df4f0663b371d1d',
'src/tools/deps2git':
'src/tools/swarming_client':
Var('git_url') + '/external/swarming.client.git@bbf1fcca7932d92cca9d7dab46ea271a7f6d61fb',
'src/v8':
- Var('git_url') + '/external/v8.git@0bb343f5ed1becef0996a234d5d7d431e60ef72a',
+ Var('git_url') + '/external/v8.git@895fe777a81b25528620c70eadc4c7463ccfd749',
}
deps_os = {
'src/third_party/libwebm/source':\r
(Var("git.chromium.org")) + '/webm/libwebm.git@0d4cb404ea4195e5e21d04db2c955615535ce62e',\r
'src/third_party/WebKit':\r
- (Var("blink")) + '/branches/chromium/2125@180871',\r
+ (Var("blink")) + '/branches/chromium/2125@182060',\r
'src/third_party/openmax_dl':\r
(Var("webrtc")) + '/deps/third_party/openmax@6777',\r
'src/third_party/libc++abi/trunk':\r
'src/media/cdm/ppapi/api':\r
'/trunk/deps/cdm@288172',\r
'src/third_party/skia':\r
- (Var("git.chromium.org")) + '/skia.git@1313b3f69370915a4fd911f97b4975d90bdf8869',\r
+ (Var("git.chromium.org")) + '/skia.git@773a327b12f653c3bb0630fee1b3ba9629f5717e',\r
'src/tools/swarming_client':\r
(Var("git.chromium.org")) + '/external/swarming.client.git@bbf1fcca7932d92cca9d7dab46ea271a7f6d61fb',\r
'src/chrome/test/data/perf/frame_rate/content':\r
'src/third_party/smhasher/src':\r
(Var("smhasher")) + '/trunk@152',\r
'src/third_party/webrtc':\r
- (Var("webrtc")) + '/branches/38/webrtc@6941',\r
+ (Var("webrtc")) + '/branches/38/webrtc@7177',\r
'build/scripts/tools/deps2git':\r
'/trunk/tools/deps2git@285662',\r
'src/third_party/hunspell_dictionaries':\r
'/trunk/deps/third_party/hunspell_dictionaries@255132',\r
'src/native_client':\r
- (Var("native_client")) + '/trunk/src/native_client@13626',\r
+ (Var("native_client")) + '/branches/2125/src/native_client@13665',\r
'src/third_party/brotli/src':\r
(Var("git.chromium.org")) + '/external/font-compression-reference.git@6cef49677dc4c650ef6e3f56041e0a41803afa8c',\r
'src/tools/page_cycler/acid3':\r
'src/third_party/cacheinvalidation/src':\r
(Var("google-cache-invalidation-api")) + '/trunk/src@335',\r
'src/chrome/test/data/extensions/api_test/permissions/nacl_enabled/bin':\r
- (Var("native_client")) + '/trunk/src/native_client/tests/prebuilt@13626',\r
+ (Var("native_client")) + '/branches/2125/src/native_client/tests/prebuilt@13665',\r
'src/third_party/leveldatabase/src':\r
(Var("leveldb")) + '/trunk@80',\r
'src/third_party/webpagereplay':\r
'src/third_party/safe_browsing/testing':\r
(Var("google-safe-browsing")) + '/trunk/testing@112',\r
'src/third_party/ffmpeg':\r
- (Var("git.chromium.org")) + '/chromium/third_party/ffmpeg.git@5961d8ac36f995dc3162aff7f8039ef7d772aaac',\r
+ (Var("git.chromium.org")) + '/chromium/third_party/ffmpeg.git@98ca32e50f6e38447bc81705d0689ebceb6ac649',\r
'build':\r
'/trunk/tools/build@290047',\r
'build/scripts/command_wrapper/bin':\r
'src/tools/deps2git':\r
'/trunk/tools/deps2git@276439',\r
'src/third_party/libjpeg_turbo':\r
- '/trunk/deps/third_party/libjpeg_turbo@272637',\r
+ '/trunk/deps/third_party/libjpeg_turbo@291725',\r
'src/third_party/libsrtp':\r
'/trunk/deps/third_party/libsrtp@283418',\r
'src/v8':\r
- (Var("v8")) + '/branches/3.28@23373',\r
+ (Var("v8")) + '/branches/3.28@23836',\r
'src/third_party/pywebsocket/src':\r
(Var("pywebsocket")) + '/trunk/src@790',\r
'src/third_party/libvpx':\r
- '/branches/2125/deps/third_party/libvpx@291144',\r
+ '/branches/2125/deps/third_party/libvpx@291831',\r
'src/third_party/boringssl/src':\r
'https://boringssl.googlesource.com/boringssl.git@c3d79605ab06cffa87877bcfe0792f767bde8b90',\r
'src/third_party/swig/Lib':\r
'../printing/printing.gyp:printing',
'../skia/skia.gyp:skia',
'../third_party/WebKit/public/blink.gyp:blink',
- '../v8/tools/gyp/v8.gyp:v8',
'../ui/gl/gl.gyp:gl',
'../ui/shell_dialogs/shell_dialogs.gyp:shell_dialogs',
'../webkit/common/gpu/webkit_gpu.gyp:webkit_gpu',
'public/browser/draw_gl.h',
'renderer/aw_content_renderer_client.cc',
'renderer/aw_content_renderer_client.h',
- 'renderer/aw_execution_termination_filter.cc',
- 'renderer/aw_execution_termination_filter.h',
'renderer/aw_key_systems.cc',
'renderer/aw_key_systems.h',
'renderer/aw_permission_client.cc',
new UserData(handler));
}
+void AwContentsClientBridgeBase::Disassociate(
+ WebContents* web_contents) {
+ web_contents->RemoveUserData(kAwContentsClientBridgeBase);
+}
+
// static
AwContentsClientBridgeBase* AwContentsClientBridgeBase::FromWebContents(
WebContents* web_contents) {
// Adds the handler to the UserData registry.
static void Associate(content::WebContents* web_contents,
AwContentsClientBridgeBase* handler);
+ static void Disassociate(content::WebContents* web_contents);
static AwContentsClientBridgeBase* FromWebContents(
content::WebContents* web_contents);
static AwContentsClientBridgeBase* FromID(int render_process_id,
bool* did_suppress_message) {
AwContentsClientBridgeBase* bridge =
AwContentsClientBridgeBase::FromWebContents(web_contents);
+ if (!bridge) {
+ callback.Run(false, base::string16());
+ return;
+ }
+
bridge->RunJavaScriptDialog(message_type,
origin_url,
message_text,
const DialogClosedCallback& callback) {
AwContentsClientBridgeBase* bridge =
AwContentsClientBridgeBase::FromWebContents(web_contents);
+ if (!bridge) {
+ callback.Run(false, base::string16());
+ return;
+ }
+
bridge->RunBeforeUnloadDialog(web_contents->GetURL(),
message_text,
callback);
if (!compositor_)
return false;
+ if (last_on_draw_global_visible_rect_.IsEmpty())
+ return client_->RequestDrawGL(java_canvas, false);
+
if (!hardware_enabled_) {
hardware_enabled_ = compositor_->InitializeHwDraw();
if (hardware_enabled_) {
FROM_HERE,
fallback_tick_fired_.callback(),
base::TimeDelta::FromMilliseconds(kFallbackTickTimeoutInMilliseconds));
+ } else {
+ // Pretend we just composited to unblock further invalidates.
+ DidComposite();
}
}
// This should only be called if OnDraw or DrawGL did not come in time, which
// means block_invalidates_ must still be true.
DCHECK(block_invalidates_);
- if (compositor_needs_continuous_invalidate_ && compositor_)
+ if (compositor_needs_continuous_invalidate_ && compositor_) {
ForceFakeCompositeSW();
+ } else {
+ // Pretend we just composited to unblock further invalidates.
+ DidComposite();
+ }
}
void BrowserViewRenderer::ForceFakeCompositeSW() {
attributes, &attribs_for_gles2);
attribs_for_gles2.lose_context_when_out_of_memory = true;
- scoped_ptr<gpu::GLInProcessContext> context(
- gpu::GLInProcessContext::Create(service,
- surface,
- surface->IsOffscreen(),
- gfx::kNullAcceleratedWidget,
- surface->GetSize(),
- share_context,
- false /* share_resources */,
- attribs_for_gles2,
- gpu_preference));
+ scoped_ptr<gpu::GLInProcessContext> context(gpu::GLInProcessContext::Create(
+ service,
+ surface,
+ surface->IsOffscreen(),
+ gfx::kNullAcceleratedWidget,
+ surface->GetSize(),
+ share_context,
+ false /* share_resources */,
+ attribs_for_gles2,
+ gpu_preference,
+ gpu::GLInProcessContextSharedMemoryLimits()));
DCHECK(context.get());
return webkit::gpu::ContextProviderInProcess::Create(
void HardwareRenderer::CommitFrame() {
scoped_ptr<DrawGLInput> input = shared_renderer_state_->PassDrawGLInput();
- if (!input.get()) {
- DLOG(WARNING) << "No frame to commit";
+ // Happens with empty global visible rect.
+ if (!input.get())
return;
- }
DCHECK(!input->frame.gl_frame_data);
DCHECK(!input->frame.software_frame_data);
return;
}
- if (!delegated_layer_.get()) {
- DLOG(ERROR) << "No frame committed";
- return;
- }
-
// TODO(boliu): Handle context loss.
if (last_egl_context_ != current_context)
DLOG(WARNING) << "EGLContextChanged";
draw_constraints);
}
+ if (!delegated_layer_.get())
+ return;
+
viewport_.SetSize(draw_info->width, draw_info->height);
layer_tree_host_->SetViewportSize(viewport_);
clip_.SetRect(draw_info->clip_left,
ApplyCmdlineOverridesToURLRequestContextBuilder(&builder);
url_request_context_.reset(builder.Build());
- channel_id_service_.reset(
- new net::ChannelIDService(
- new net::DefaultChannelIDStore(NULL),
- base::WorkerPool::GetTaskRunner(true)));
- url_request_context_->set_channel_id_service(channel_id_service_.get());
// TODO(mnaganov): Fix URLRequestContextBuilder to use proper threads.
net::HttpNetworkSession::Params network_session_params;
#include "base/memory/scoped_ptr.h"
#include "content/public/browser/content_browser_client.h"
#include "net/http/http_network_session.h"
-#include "net/ssl/channel_id_service.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_job_factory.h"
data_reduction_proxy_auth_request_handler_;
scoped_ptr<net::URLRequestJobFactory> job_factory_;
scoped_ptr<net::HttpTransactionFactory> main_http_factory_;
- scoped_ptr<net::ChannelIDService> channel_id_service_;
// ProtocolHandlers and interceptors are stored here between
// SetHandlersAndInterceptors() and the first GetURLRequestContext() call.
Send(new AwViewMsg_SetJsOnlineProperty(network_up));
}
-void AwRenderViewHostExt::SendCheckRenderThreadResponsiveness() {
- Send(new AwViewMsg_CheckRenderThreadResponsiveness());
-}
-
void AwRenderViewHostExt::RenderViewCreated(
content::RenderViewHost* render_view_host) {
Send(new AwViewMsg_SetBackgroundColor(web_contents()->GetRoutingID(),
void SetBackgroundColor(SkColor c);
void SetJsOnlineProperty(bool network_up);
- void SendCheckRenderThreadResponsiveness();
-
private:
// content::WebContentsObserver implementation.
virtual void RenderViewCreated(content::RenderViewHost* view_host) OVERRIDE;
IPC_MESSAGE_CONTROL1(AwViewMsg_SetJsOnlineProperty,
bool /* network_up */)
-// Sent prior to making a navigation via loadUrl to make sure that
-// render thread isn't stuck in a loop induced by JavaScript code.
-IPC_MESSAGE_CONTROL0(AwViewMsg_CheckRenderThreadResponsiveness)
-
//-----------------------------------------------------------------------------
// RenderView messages
// These are messages sent from the renderer to the browser process.
@CalledByNative
public static long[] openAsset(Context context, String fileName) {
+ AssetFileDescriptor afd = null;
try {
AssetManager manager = context.getAssets();
- AssetFileDescriptor afd = manager.openFd(fileName);
+ afd = manager.openFd(fileName);
return new long[] { afd.getParcelFileDescriptor().detachFd(),
afd.getStartOffset(),
afd.getLength() };
} catch (IOException e) {
- Log.e(LOGTAG, "Error while loading asset " + fileName + ": " + e.getMessage());
+ Log.e(LOGTAG, "Error while loading asset " + fileName + ": " + e);
return new long[] {-1, -1, -1};
+ } finally {
+ try {
+ if (afd != null) {
+ afd.close();
+ }
+ } catch (IOException e2) {
+ Log.e(LOGTAG, "Unable to close AssetFileDescriptor", e2);
+ }
}
}
}
private final AwLayoutChangeListener mLayoutChangeListener;
private final Context mContext;
private ContentViewCore mContentViewCore;
+ private WindowAndroid mWindowAndroid;
private final AwContentsClient mContentsClient;
private final AwContentViewClient mContentViewClient;
private WebContentsObserverAndroid mWebContentsObserver;
Context context, InternalAccessDelegate internalDispatcher, long nativeWebContents,
GestureStateListener gestureStateListener,
ContentViewClient contentViewClient,
- ContentViewCore.ZoomControlsDelegate zoomControlsDelegate) {
+ ContentViewCore.ZoomControlsDelegate zoomControlsDelegate,
+ WindowAndroid windowAndroid) {
ContentViewCore contentViewCore = new ContentViewCore(context);
contentViewCore.initialize(containerView, internalDispatcher, nativeWebContents,
- context instanceof Activity ?
- new ActivityWindowAndroid((Activity) context) :
- new WindowAndroid(context.getApplicationContext()));
+ windowAndroid);
contentViewCore.addGestureStateListener(gestureStateListener);
contentViewCore.setContentViewClient(contentViewClient);
contentViewCore.setZoomControlsDelegate(zoomControlsDelegate);
mCleanupReference = new CleanupReference(this, new DestroyRunnable(mNativeAwContents));
long nativeWebContents = nativeGetWebContents(mNativeAwContents);
+
+ mWindowAndroid = mContext instanceof Activity ?
+ new ActivityWindowAndroid((Activity) mContext) :
+ new WindowAndroid(mContext.getApplicationContext());
mContentViewCore = createAndInitializeContentViewCore(
mContainerView, mContext, mInternalAccessAdapter, nativeWebContents,
- new AwGestureStateListener(), mContentViewClient, mZoomControls);
+ new AwGestureStateListener(), mContentViewClient, mZoomControls, mWindowAndroid);
nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge,
mIoThreadClient, mInterceptNavigationDelegate);
installWebContentsObserver();
* @param params Parameters for this load.
*/
public void loadUrl(LoadUrlParams params) {
- if (mNativeAwContents == 0) return;
-
if (params.getLoadUrlType() == LoadUrlParams.LOAD_TYPE_DATA &&
!params.isBaseUrlDataScheme()) {
// This allows data URLs with a non-data base URL access to file:///android_asset/ and
}
}
- nativeSetExtraHeadersForUrl(
- mNativeAwContents, params.getUrl(), params.getExtraHttpRequestHeadersString());
+ if (mNativeAwContents != 0) {
+ nativeSetExtraHeadersForUrl(
+ mNativeAwContents, params.getUrl(), params.getExtraHttpRequestHeadersString());
+ }
params.setExtraHeaders(new HashMap<String, String>());
- nativeSendCheckRenderThreadResponsiveness(mNativeAwContents);
mContentViewCore.loadUrl(params);
// The behavior of WebViewClassic uses the populateVisitedLinks callback in WebKit.
@CalledByNative
private void postInvalidateOnAnimation() {
- if (SUPPORTS_ON_ANIMATION) {
+ if (SUPPORTS_ON_ANIMATION && !mWindowAndroid.isInsideVSync()) {
mContainerView.postInvalidateOnAnimation();
} else {
- mContainerView.postInvalidate();
+ mContainerView.invalidate();
}
}
+ // Call postInvalidateOnAnimation for invalidations. This is only used to synchronize
+ // draw functor destruction.
+ @CalledByNative
+ private void invalidateOnFunctorDestroy() {
+ mContainerView.invalidate();
+ }
+
@CalledByNative
private int[] getLocationOnScreen() {
int[] result = new int[2];
private native void nativeClearView(long nativeAwContents);
private native void nativeSetExtraHeadersForUrl(long nativeAwContents,
String url, String extraHeaders);
- private native void nativeSendCheckRenderThreadResponsiveness(long nativeAwContents);
private native void nativeInvokeGeolocationCallback(
long nativeAwContents, boolean value, String requestingFrame);
--- /dev/null
+// Copyright 2014 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.test;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.android_webview.AwContents;
+import org.chromium.base.test.util.Feature;
+import org.chromium.content.browser.JavascriptInterface;
+
+/**
+ * Test suite for the WebView specific JavaBridge features.
+ */
+public class AwJavaBridgeTest extends AwTestBase {
+
+ private TestAwContentsClient mContentsClient = new TestAwContentsClient();
+ private AwTestContainerView mTestContainerView;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTestContainerView = createAwTestContainerViewOnMainSync(mContentsClient);
+ }
+
+ @SmallTest
+ @Feature({"AndroidWebView", "Android-JavaBridge"})
+ public void testDestroyFromJavaObject() throws Throwable {
+ final String HTML = "<html>Hello World</html>";
+ final TestAwContentsClient client2 = new TestAwContentsClient();
+ final AwTestContainerView view2 = createAwTestContainerViewOnMainSync(client2);
+ final AwContents awContents = mTestContainerView.getAwContents();
+
+ class Test {
+ @JavascriptInterface
+ public void destroy() {
+ try {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ awContents.destroy();
+ }
+ });
+ // Destroying one AwContents from within the JS callback should still
+ // leave others functioning.
+ loadDataSync(view2.getAwContents(), client2.getOnPageFinishedHelper(),
+ HTML, "text/html", false);
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+ }
+
+ enableJavaScriptOnUiThread(awContents);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ awContents.addPossiblyUnsafeJavascriptInterface(new Test(), "test", null);
+ }
+ });
+
+ loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), HTML,
+ "text/html", false);
+
+ // Ensure the JS interface object is there, and invoke the test method.
+ assertEquals("\"function\"", executeJavaScriptAndWaitForResult(
+ awContents, mContentsClient, "typeof test.destroy"));
+ awContents.evaluateJavaScript("test.destroy()", null);
+
+ client2.getOnPageFinishedHelper().waitForCallback(
+ client2.getOnPageFinishedHelper().getCallCount());
+ }
+}
package org.chromium.android_webview.test;
-import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
-import junit.framework.Assert;
-
import org.apache.http.Header;
import org.apache.http.HttpRequest;
import org.chromium.android_webview.AwContents;
if (webServer != null) webServer.shutdown();
}
}
-
- private static class TestController {
- private final Object mLock = new Object();
- private boolean mIsReady = false;
- public void notifyPageIsReady() {
- synchronized (mLock) {
- mIsReady = true;
- mLock.notify();
- }
- }
- public void waitUntilIsReady() {
- synchronized (mLock) {
- while (!mIsReady) {
- try {
- mLock.wait(WAIT_TIMEOUT_MS);
- } catch (Exception e) {
- continue;
- }
- if (!mIsReady) {
- Assert.fail("Wait timed out");
- }
- }
- mIsReady = false;
- }
- }
- }
-
- // Verify that it is possible to interrupt JS scripts stuck in an infinite loop
- // by calling loadUrl on a WebView.
- @LargeTest
- @Feature({"AndroidWebView"})
- public void testLoadUrlInterruptsLoopedScripts() throws Throwable {
- final String infiniteLoopPage =
- "<html><head>" +
- " <script>" +
- " function infiniteLoop() {" +
- " test.notifyPageIsReady();" +
- " while(1);" +
- " }" +
- " </script>" +
- "</head><body onload='setTimeout(infiniteLoop, 0)'>" +
- "</body></html>";
- final String simplePage = "<html><body onload='test.notifyPageIsReady()'></body></html>";
- final String expectedTitle = "PASS";
- final String pageWithTitle = "<html><body onload='document.title=\"" + expectedTitle +
- "\"; test.notifyPageIsReady()'></body></html>";
-
- final AwTestContainerView testContainerView =
- createAwTestContainerViewOnMainSync(new TestAwContentsClient());
- final AwContents awContents = testContainerView.getAwContents();
- getAwSettingsOnUiThread(awContents).setJavaScriptEnabled(true);
- final TestController testController = new TestController();
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- awContents.addPossiblyUnsafeJavascriptInterface(testController, "test", null);
- }
- });
- loadDataAsync(awContents, infiniteLoopPage, "text/html", false);
- testController.waitUntilIsReady();
- loadDataAsync(awContents, simplePage, "text/html", false);
- testController.waitUntilIsReady();
- // Load another page that runs JS to make sure that the WebView is still functional.
- loadDataAsync(awContents, pageWithTitle, "text/html", false);
- testController.waitUntilIsReady();
- assertEquals(expectedTitle, getTitleOnUiThread(awContents));
- }
}
#include "android_webview/native/android_webview_jni_registrar.h"
#include "base/android/jni_android.h"
#include "base/android/jni_registrar.h"
+#include "base/android/jni_utils.h"
#include "base/android/library_loader/library_loader_hooks.h"
#include "components/navigation_interception/component_jni_registrar.h"
#include "components/web_contents_delegate_android/component_jni_registrar.h"
if (!android_webview::RegisterJni(env))
return -1;
+ base::android::InitReplacementClassLoader(env,
+ base::android::GetClassLoader(env));
+
content::SetContentMainDelegate(new android_webview::AwMainDelegate());
// Initialize url lib here while we are still single-threaded, in case we use
// the java peer. This is important for the popup window case, where we are
// swapping AwContents out that share the same java AwContentsClientBridge.
// See b/15074651.
+ AwContentsClientBridgeBase::Disassociate(web_contents_.get());
contents_client_bridge_.reset();
// We do not delete AwContents immediately. Some applications try to delete
void AwContents::ReleaseHardwareDrawIfNeeded() {
InsideHardwareReleaseReset inside_reset(&shared_renderer_state_);
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
+ if (!obj.is_null())
+ Java_AwContents_invalidateOnFunctorDestroy(env, obj.obj());
+
bool hardware_initialized = browser_view_renderer_.hardware_enabled();
if (hardware_initialized) {
bool draw_functor_succeeded = RequestDrawGL(NULL, true);
extra_headers);
}
-void AwContents::SendCheckRenderThreadResponsiveness(JNIEnv* env, jobject obj) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- render_view_host_ext_->SendCheckRenderThreadResponsiveness();
-}
-
void AwContents::SetJsOnlineProperty(JNIEnv* env,
jobject obj,
jboolean network_up) {
void ClearView(JNIEnv* env, jobject obj);
void SetExtraHeadersForUrl(JNIEnv* env, jobject obj,
jstring url, jstring extra_headers);
- void SendCheckRenderThreadResponsiveness(JNIEnv* env, jobject obj);
void DrawGL(AwDrawGLInfo* draw_info);
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
- if (obj.is_null())
+ if (obj.is_null()) {
+ callback.Run(false, base::string16());
return;
+ }
int callback_id = pending_js_dialog_callbacks_.Add(
new content::JavaScriptDialogManager::DialogClosedCallback(callback));
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
- if (obj.is_null())
+ if (obj.is_null()) {
+ callback.Run(false, base::string16());
return;
+ }
int callback_id = pending_js_dialog_callbacks_.Add(
new content::JavaScriptDialogManager::DialogClosedCallback(callback));
#include "android_webview/common/aw_resource.h"
#include "android_webview/common/render_view_messages.h"
#include "android_webview/common/url_constants.h"
-#include "android_webview/renderer/aw_execution_termination_filter.h"
#include "android_webview/renderer/aw_key_systems.h"
#include "android_webview/renderer/aw_permission_client.h"
#include "android_webview/renderer/aw_render_frame_ext.h"
#include "third_party/WebKit/public/platform/WebURLError.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
#include "third_party/WebKit/public/web/WebFrame.h"
-#include "third_party/WebKit/public/web/WebKit.h"
#include "third_party/WebKit/public/web/WebNavigationType.h"
#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
#include "url/gurl.h"
visited_link_slave_.reset(new visitedlink::VisitedLinkSlave);
thread->AddObserver(visited_link_slave_.get());
-
- execution_termination_filter_ = new AwExecutionTerminationFilter(
- thread->GetIOMessageLoopProxy(),
- thread->GetMessageLoop()->message_loop_proxy());
- thread->AddFilter(execution_termination_filter_.get());
- thread->AddObserver(this);
-}
-
-void AwContentRendererClient::WebKitInitialized() {
- execution_termination_filter_->SetRenderThreadIsolate(
- blink::mainThreadIsolate());
}
bool AwContentRendererClient::HandleNavigation(
namespace android_webview {
-class AwExecutionTerminationFilter;
-
-class AwContentRendererClient : public content::ContentRendererClient,
- public content::RenderProcessObserver {
+class AwContentRendererClient : public content::ContentRendererClient {
public:
AwContentRendererClient();
virtual ~AwContentRendererClient();
blink::WebNavigationPolicy default_policy,
bool is_redirect) OVERRIDE;
- // content::RenderProcessObserver implementation.
- virtual void WebKitInitialized() OVERRIDE;
-
private:
scoped_ptr<AwRenderProcessObserver> aw_render_process_observer_;
scoped_ptr<visitedlink::VisitedLinkSlave> visited_link_slave_;
- scoped_refptr<AwExecutionTerminationFilter> execution_termination_filter_;
};
} // namespace android_webview
+++ /dev/null
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "android_webview/renderer/aw_execution_termination_filter.h"
-
-#include <v8.h>
-
-#include "android_webview/common/render_view_messages.h"
-#include "base/message_loop/message_loop_proxy.h"
-
-namespace {
-const int kTerminationTimeoutInSeconds = 3;
-} // namespace
-
-namespace android_webview {
-
-AwExecutionTerminationFilter::AwExecutionTerminationFilter(
- const scoped_refptr<base::MessageLoopProxy>& io_message_loop,
- const scoped_refptr<base::MessageLoopProxy>& main_message_loop)
- : io_message_loop_(io_message_loop),
- main_message_loop_(main_message_loop),
- render_thread_isolate_(NULL) {
-}
-
-AwExecutionTerminationFilter::~AwExecutionTerminationFilter() {
-}
-
-void AwExecutionTerminationFilter::SetRenderThreadIsolate(
- v8::Isolate* isolate) {
- render_thread_isolate_ = isolate;
-}
-
-bool AwExecutionTerminationFilter::OnMessageReceived(
- const IPC::Message& message) {
- bool handled = true;
- IPC_BEGIN_MESSAGE_MAP(AwExecutionTerminationFilter, message)
- IPC_MESSAGE_HANDLER(AwViewMsg_CheckRenderThreadResponsiveness,
- OnCheckRenderThreadResponsiveness)
- IPC_MESSAGE_UNHANDLED(handled = false)
- IPC_END_MESSAGE_MAP()
- return handled;
-}
-
-void AwExecutionTerminationFilter::OnCheckRenderThreadResponsiveness() {
- termination_timer_.Start(
- FROM_HERE,
- base::TimeDelta::FromSeconds(kTerminationTimeoutInSeconds),
- base::Bind(&AwExecutionTerminationFilter::TerminateExecution, this));
- // Post a request to stop the timer via render thread's message loop
- // to ensure that render thread is responsive.
- main_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&AwExecutionTerminationFilter::StopTimerOnMainThread,
- this));
-}
-
-void AwExecutionTerminationFilter::StopTimerOnMainThread() {
- io_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&AwExecutionTerminationFilter::StopTimer, this));
-}
-
-void AwExecutionTerminationFilter::StopTimer() {
- termination_timer_.Stop();
-}
-
-void AwExecutionTerminationFilter::TerminateExecution() {
- if (render_thread_isolate_) {
- LOG(WARNING) << "Trying to terminate JavaScript execution because "
- "renderer is unresponsive";
- v8::V8::TerminateExecution(render_thread_isolate_);
- }
-}
-
-} // namespace android_webview
+++ /dev/null
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ANDROID_WEBVIEW_RENDERER_AW_EXECUTION_TERMINATION_FILTER_H_
-#define ANDROID_WEBVIEW_RENDERER_AW_EXECUTION_TERMINATION_FILTER_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "base/timer/timer.h"
-#include "ipc/message_filter.h"
-
-namespace base {
-class MessageLoopProxy;
-}
-
-namespace v8 {
-class Isolate;
-}
-
-namespace android_webview {
-
-// The purpose of AwExecutionTerminationFilter is to attempt to terminate
-// any JavaScript code that is stuck in a loop before doing a navigation
-// originating from a Andoird WebView URL loading functions.
-//
-// This is how it works. AwExecutionTerminationFilter is created on render
-// thread. It listens on IO thread for navigation requests coming from
-// AwContents.loadUrl calls. On each such a request, it posts a delayed
-// cancellable task on the IO thread's message loop and, at the same time, posts
-// a cancellation task on the render thread's message loop. If render thread
-// is not stuck, the cancellation task runs and cancels the delayed task.
-// Otherwise, the delayed task runs and terminates execution of JS code
-// from the IO thread.
-class AwExecutionTerminationFilter : public IPC::MessageFilter {
- public:
- AwExecutionTerminationFilter(
- const scoped_refptr<base::MessageLoopProxy>& io_message_loop,
- const scoped_refptr<base::MessageLoopProxy>& main_message_loop);
-
- void SetRenderThreadIsolate(v8::Isolate* isolate);
-
- private:
- virtual ~AwExecutionTerminationFilter();
-
- // IPC::MessageFilter methods.
- virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
-
- void OnCheckRenderThreadResponsiveness();
- void StopTimerOnMainThread();
- void StopTimer();
- void TerminateExecution();
-
- const scoped_refptr<base::MessageLoopProxy> io_message_loop_;
- const scoped_refptr<base::MessageLoopProxy> main_message_loop_;
-
- v8::Isolate* render_thread_isolate_;
- base::OneShotTimer<AwExecutionTerminationFilter> termination_timer_;
-
- DISALLOW_COPY_AND_ASSIGN(AwExecutionTerminationFilter);
-};
-
-} // namespace android_webview
-
-#endif // ANDROID_WEBVIEW_RENDERER_AW_EXECUTION_TERMINATION_FILTER_H_
#include "ash/shell_delegate.h"
#include "ash/wm/coordinate_conversion.h"
#include "base/command_line.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/aura/client/capture_client.h"
virtual_keyboard_window_controller_.reset();
Shell::GetScreen()->RemoveObserver(this);
- // Delete all root window controllers, which deletes root window
- // from the last so that the primary root window gets deleted last.
- for (WindowTreeHostMap::const_reverse_iterator it =
- window_tree_hosts_.rbegin();
- it != window_tree_hosts_.rend();
- ++it) {
- RootWindowController* controller =
- GetRootWindowController(GetWindow(it->second));
- DCHECK(controller);
- delete controller;
+
+ int64 primary_id = Shell::GetScreen()->GetPrimaryDisplay().id();
+
+ // Delete non primary root window controllers first, then
+ // delete the primary root window controller.
+ aura::Window::Windows root_windows = DisplayController::GetAllRootWindows();
+ std::vector<RootWindowController*> to_delete;
+ RootWindowController* primary_rwc = NULL;
+ for (aura::Window::Windows::iterator iter = root_windows.begin();
+ iter != root_windows.end();
+ ++iter) {
+ RootWindowController* rwc = GetRootWindowController(*iter);
+ if (GetRootWindowSettings(*iter)->display_id == primary_id)
+ primary_rwc = rwc;
+ else
+ to_delete.push_back(rwc);
}
+ CHECK(primary_rwc);
+
+ STLDeleteElements(&to_delete);
+ delete primary_rwc;
}
void DisplayController::CreatePrimaryHost(
}
aura::Window* DisplayController::GetRootWindowForDisplayId(int64 id) {
- DCHECK_EQ(1u, window_tree_hosts_.count(id));
+ CHECK_EQ(1u, window_tree_hosts_.count(id));
AshWindowTreeHost* host = window_tree_hosts_[id];
CHECK(host);
return GetWindow(host);
}
void Shell::ToggleAppList(aura::Window* window) {
- if (GetAppListTargetVisibility()) {
+ if (app_list_controller_ && app_list_controller_->IsVisible()) {
DismissAppList();
return;
}
const gfx::Display& display) {
screen_ = screen;
display_id_ = display.id();
+ work_area_ = display.work_area();
root_window_ = ash::Shell::GetInstance()->display_controller()->
GetRootWindowForDisplayId(display_id_);
UpdateShelf();
}
views::TrayBubbleView* bubble_view = views::TrayBubbleView::Create(
tray->GetBubbleWindowContainer(), anchor, tray, &init_params);
- bubble_view->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
bubble_wrapper_.reset(new TrayBubbleWrapper(tray, bubble_view));
+ bubble_view->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
bubble->InitializeContents(bubble_view);
}
#include "ui/aura/window_tree_host.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/cursor/image_cursors.h"
+#include "ui/base/layout.h"
namespace ash {
namespace {
DCHECK(display.is_valid());
// Use the platform's device scale factor instead of the display's, which
// might have been adjusted for the UI scale.
- const float scale_factor = Shell::GetInstance()->display_manager()->
+ const float original_scale = Shell::GetInstance()->display_manager()->
GetDisplayInfo(display.id()).device_scale_factor();
- if (image_cursors_->SetDisplay(display, scale_factor))
+#if defined(OS_CHROMEOS)
+ // And use the nearest resource scale factor.
+ const float cursor_scale = ui::GetScaleForScaleFactor(
+ ui::GetSupportedScaleFactor(original_scale));
+#else
+ // TODO(oshima): crbug.com/143619
+ const float cursor_scale = original_scale;
+#endif
+ if (image_cursors_->SetDisplay(display, cursor_scale))
SetCursor(delegate->GetCursor(), delegate);
#if defined(OS_CHROMEOS)
Shell::GetInstance()->display_controller()->cursor_window_controller()->
EXPECT_EQ(gfx::Display::ROTATE_270, test_api.GetCurrentCursorRotation());
}
+#if defined(OS_CHROMEOS)
+// TODO(oshima): crbug.com/143619
+TEST_F(AshNativeCursorManagerTest, FractionalScale) {
+ ::wm::CursorManager* cursor_manager = Shell::GetInstance()->cursor_manager();
+ CursorManagerTestApi test_api(cursor_manager);
+ // Cursor should use the resource scale factor.
+ UpdateDisplay("800x100*1.25");
+ EXPECT_EQ(1.0f, test_api.GetCurrentCursor().device_scale_factor());
+}
+#endif
+
TEST_F(AshNativeCursorManagerTest, UIScaleShouldNotChangeCursor) {
int64 display_id = Shell::GetScreen()->GetPrimaryDisplay().id();
gfx::Display::SetInternalDisplayId(display_id);
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "ash/display/display_manager.h"
#include "ash/root_window_controller.h"
#include "ash/screen_util.h"
#include "ash/shell.h"
}
TEST_F(LockLayoutManagerTest, KeyboardBounds) {
- gfx::Rect screen_bounds = Shell::GetScreen()->GetPrimaryDisplay().bounds();
+ gfx::Display primary_display = Shell::GetScreen()->GetPrimaryDisplay();
+ gfx::Rect screen_bounds = primary_display.bounds();
views::Widget::InitParams widget_params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_ENABLED);
ShowKeyboard(true);
EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
+ gfx::Rect keyboard_bounds =
+ keyboard::KeyboardController::GetInstance()->current_keyboard_bounds();
+ EXPECT_NE(keyboard_bounds, gfx::Rect());
ShowKeyboard(false);
+ // When keyboard is hidden make sure that rotating the screen gives 100% of
+ // screen size to window.
+ // Repro steps for http://crbug.com/401667:
+ // 1. Set up login screen defaults: VK override disabled
+ // 2. Show/hide keyboard, make sure that no stale keyboard bounds are cached.
+ keyboard::SetKeyboardOverscrollOverride(
+ keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_DISABLED);
+ ShowKeyboard(true);
+ ShowKeyboard(false);
+ ash::DisplayManager* display_manager =
+ Shell::GetInstance()->display_manager();
+ display_manager->SetDisplayRotation(primary_display.id(),
+ gfx::Display::ROTATE_90);
+ primary_display = Shell::GetScreen()->GetPrimaryDisplay();
+ screen_bounds = primary_display.bounds();
+ EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
+ display_manager->SetDisplayRotation(primary_display.id(),
+ gfx::Display::ROTATE_0);
+
// When virtual keyboard overscroll is disabled keyboard bounds do
// affect window bounds.
keyboard::SetKeyboardOverscrollOverride(
ShowKeyboard(true);
keyboard::KeyboardController* keyboard =
keyboard::KeyboardController::GetInstance();
+ primary_display = Shell::GetScreen()->GetPrimaryDisplay();
+ screen_bounds = primary_display.bounds();
gfx::Rect target_bounds(screen_bounds);
target_bounds.set_height(target_bounds.height() -
keyboard->proxy()->GetKeyboardWindow()->bounds().height());
keyboard::KeyboardController::GetInstance();
gfx::Rect keyboard_bounds;
- if (keyboard_controller && !keyboard::IsKeyboardOverscrollEnabled())
+ if (keyboard_controller &&
+ !keyboard::IsKeyboardOverscrollEnabled() &&
+ keyboard_controller->keyboard_visible()) {
keyboard_bounds = keyboard_controller->current_keyboard_bounds();
+ }
gfx::Rect bounds =
ScreenUtil::GetDisplayBoundsInParent(window_state->window());
bounds.set_height(bounds.height() - keyboard_bounds.height());
+
+ VLOG(1) << "Updating window bounds to: " << bounds.ToString();
window_state->SetBoundsDirect(bounds);
}
"android/jni_registrar.h",
"android/jni_string.cc",
"android/jni_string.h",
+ "android/jni_utils.cc",
+ "android/jni_utils.h",
"android/jni_weak_ref.cc",
"android/jni_weak_ref.h",
"android/library_loader/library_loader_hooks.cc",
"android/java/src/org/chromium/base/EventLog.java",
"android/java/src/org/chromium/base/FieldTrialList.java",
"android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
+ "android/java/src/org/chromium/base/JNIUtils.java",
"android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
"android/java/src/org/chromium/base/MemoryPressureListener.java",
"android/java/src/org/chromium/base/JavaHandlerThread.java",
#include "base/android/java_handler_thread.h"
#include "base/android/jni_android.h"
#include "base/android/jni_registrar.h"
+#include "base/android/jni_utils.h"
#include "base/android/memory_pressure_listener_android.h"
#include "base/android/path_service_android.h"
#include "base/android/path_utils.h"
{ "FieldTrialList", base::android::RegisterFieldTrialList },
{ "ImportantFileWriterAndroid",
base::android::RegisterImportantFileWriterAndroid },
+ { "JNIUtils", base::android::RegisterJNIUtils },
{ "MemoryPressureListenerAndroid",
base::android::MemoryPressureListenerAndroid::Register },
{ "JavaHandlerThread", base::android::JavaHandlerThread::RegisterBindings },
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import java.io.File;
+
/**
* This class provides methods to access content URI schemes.
*/
public abstract class ContentUriUtils {
private static final String TAG = "ContentUriUtils";
+ private static FileProviderUtil sFileProviderUtil;
+
+ /**
+ * Provides functionality to translate a file into a content URI for use
+ * with a content provider.
+ */
+ public interface FileProviderUtil {
+ /**
+ * Generate a content uri from the given file.
+ * @param context Application context.
+ * @param file The file to be translated.
+ */
+ public Uri getContentUriFromFile(Context context, File file);
+ }
// Prevent instantiation.
private ContentUriUtils() {}
+ public static void setFileProviderUtil(FileProviderUtil util) {
+ sFileProviderUtil = util;
+ }
+
+ public static Uri getContentUriFromFile(Context context, File file) {
+ ThreadUtils.assertOnUiThread();
+ if (sFileProviderUtil != null) {
+ return sFileProviderUtil.getContentUriFromFile(context, file);
+ }
+ return null;
+ }
+
/**
* Opens the content URI for reading, and returns the file descriptor to
* the caller. The caller is responsible for closing the file desciptor.
--- /dev/null
+// Copyright 2014 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.base;
+
+/**
+ * This class provides JNI-related methods to the native library.
+ */
+public class JNIUtils {
+ /**
+ * This returns a ClassLoader that is capable of loading Chromium Java code. Such a ClassLoader
+ * is needed for the few cases where the JNI mechanism is unable to automatically determine the
+ * appropriate ClassLoader instance.
+ */
+ @CalledByNative
+ public static Object getClassLoader() {
+ return JNIUtils.class.getClassLoader();
+ }
+}
mRelroStart = in.readLong();
mRelroSize = in.readLong();
ParcelFileDescriptor fd = in.readFileDescriptor();
- mRelroFd = fd.detachFd();
+ // If CreateSharedRelro fails, the OS file descriptor will be -1 and |fd| will be null.
+ mRelroFd = (fd == null) ? -1 : fd.detachFd();
}
// from Parcelable
#include "base/android/build_info.h"
#include "base/android/jni_string.h"
+#include "base/android/jni_utils.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
// that may still be running at shutdown. There is no harm in doing this.
base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
g_application_context = LAZY_INSTANCE_INITIALIZER;
+base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky
+ g_class_loader = LAZY_INSTANCE_INITIALIZER;
+jmethodID g_class_loader_load_class_method_id = 0;
std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
ScopedJavaLocalRef<jclass> throwable_clazz =
g_application_context.Get().Reset(context);
}
+void InitReplacementClassLoader(JNIEnv* env,
+ const JavaRef<jobject>& class_loader) {
+ DCHECK(g_class_loader.Get().is_null());
+ DCHECK(!class_loader.is_null());
+
+ ScopedJavaLocalRef<jclass> class_loader_clazz =
+ GetClass(env, "java/lang/ClassLoader");
+ CHECK(!ClearException(env));
+ g_class_loader_load_class_method_id =
+ env->GetMethodID(class_loader_clazz.obj(),
+ "loadClass",
+ "(Ljava/lang/String;)Ljava/lang/Class;");
+ CHECK(!ClearException(env));
+
+ DCHECK(env->IsInstanceOf(class_loader.obj(), class_loader_clazz.obj()));
+ g_class_loader.Get().Reset(class_loader);
+}
+
const jobject GetApplicationContext() {
DCHECK(!g_application_context.Get().is_null());
return g_application_context.Get().obj();
}
ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
- jclass clazz = env->FindClass(class_name);
+ jclass clazz;
+ if (!g_class_loader.Get().is_null()) {
+ clazz = static_cast<jclass>(
+ env->CallObjectMethod(g_class_loader.Get().obj(),
+ g_class_loader_load_class_method_id,
+ ConvertUTF8ToJavaString(env, class_name).obj()));
+ } else {
+ clazz = env->FindClass(class_name);
+ }
CHECK(!ClearException(env) && clazz) << "Failed to find class " << class_name;
return ScopedJavaLocalRef<jclass>(env, clazz);
}
+jclass LazyGetClass(
+ JNIEnv* env,
+ const char* class_name,
+ base::subtle::AtomicWord* atomic_class_id) {
+ COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jclass),
+ AtomicWord_SmallerThan_jMethodID);
+ subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_class_id);
+ if (value)
+ return reinterpret_cast<jclass>(value);
+ ScopedJavaGlobalRef<jclass> clazz;
+ clazz.Reset(GetClass(env, class_name));
+ subtle::AtomicWord null_aw = reinterpret_cast<subtle::AtomicWord>(NULL);
+ subtle::AtomicWord cas_result = base::subtle::Release_CompareAndSwap(
+ atomic_class_id,
+ null_aw,
+ reinterpret_cast<subtle::AtomicWord>(clazz.obj()));
+ if (cas_result == null_aw) {
+ // We intentionally leak the global ref since we now storing it as a raw
+ // pointer in |atomic_class_id|.
+ return clazz.Release();
+ } else {
+ return reinterpret_cast<jclass>(cas_result);
+ }
+}
+
template<MethodID::Type type>
jmethodID MethodID::Get(JNIEnv* env,
jclass clazz,
BASE_EXPORT void InitApplicationContext(JNIEnv* env,
const JavaRef<jobject>& context);
+// Initializes the global ClassLoader used by the GetClass and LazyGetClass
+// methods. This is needed because JNI will use the base ClassLoader when there
+// is no Java code on the stack. The base ClassLoader doesn't know about any of
+// the application classes and will fail to lookup anything other than system
+// classes.
+BASE_EXPORT void InitReplacementClassLoader(
+ JNIEnv* env,
+ const JavaRef<jobject>& class_loader);
+
// Gets a global ref to the application context set with
// InitApplicationContext(). Ownership is retained by the function - the caller
// must NOT release it.
BASE_EXPORT ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env,
const char* class_name);
+// The method will initialize |atomic_class_id| to contain a global ref to the
+// class. And will return that ref on subsequent calls. It's the caller's
+// responsibility to release the ref when it is no longer needed.
+// The caller is responsible to zero-initialize |atomic_method_id|.
+// It's fine to simultaneously call this on multiple threads referencing the
+// same |atomic_method_id|.
+BASE_EXPORT jclass LazyGetClass(
+ JNIEnv* env,
+ const char* class_name,
+ base::subtle::AtomicWord* atomic_class_id);
+
// This class is a wrapper for JNIEnv Get(Static)MethodID.
class BASE_EXPORT MethodID {
public:
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 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.
"org/chromium/example/jni_generator/SampleForTests$InnerStructB";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_InnerStructA_clazz = NULL;
+#define InnerStructA_clazz(env) g_InnerStructA_clazz
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_SampleForTests_clazz = NULL;
+#define SampleForTests_clazz(env) g_SampleForTests_clazz
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_InnerStructB_clazz = NULL;
+#define InnerStructB_clazz(env) g_InnerStructB_clazz
} // namespace
JniIntWrapper bar) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_SampleForTests_clazz, 0);
+ SampleForTests_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_SampleForTests_clazz,
+ env, SampleForTests_clazz(env),
"javaMethod",
"("
static base::subtle::AtomicWord g_SampleForTests_staticJavaMethod = 0;
static jboolean Java_SampleForTests_staticJavaMethod(JNIEnv* env) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_SampleForTests_clazz,
- g_SampleForTests_clazz, false);
+ CHECK_CLAZZ(env, SampleForTests_clazz(env),
+ SampleForTests_clazz(env), false);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_SampleForTests_clazz,
+ env, SampleForTests_clazz(env),
"staticJavaMethod",
"("
&g_SampleForTests_staticJavaMethod);
jboolean ret =
- env->CallStaticBooleanMethod(g_SampleForTests_clazz,
+ env->CallStaticBooleanMethod(SampleForTests_clazz(env),
method_id);
jni_generator::CheckException(env);
return ret;
obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_SampleForTests_clazz);
+ SampleForTests_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_SampleForTests_clazz,
+ env, SampleForTests_clazz(env),
"packagePrivateJavaMethod",
"("
obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_SampleForTests_clazz);
+ SampleForTests_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_SampleForTests_clazz,
+ env, SampleForTests_clazz(env),
"methodThatThrowsException",
"("
JniIntWrapper i,
jstring s) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_InnerStructA_clazz,
- g_InnerStructA_clazz, NULL);
+ CHECK_CLAZZ(env, InnerStructA_clazz(env),
+ InnerStructA_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_InnerStructA_clazz,
+ env, InnerStructA_clazz(env),
"create",
"("
&g_InnerStructA_create);
jobject ret =
- env->CallStaticObjectMethod(g_InnerStructA_clazz,
+ env->CallStaticObjectMethod(InnerStructA_clazz(env),
method_id, l, as_jint(i), s);
jni_generator::CheckException(env);
return base::android::ScopedJavaLocalRef<jobject>(env, ret);
{
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_SampleForTests_clazz);
+ SampleForTests_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_SampleForTests_clazz,
+ env, SampleForTests_clazz(env),
"addStructA",
"("
{
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_SampleForTests_clazz);
+ SampleForTests_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_SampleForTests_clazz,
+ env, SampleForTests_clazz(env),
"iterateAndDoSomething",
"("
static jlong Java_InnerStructB_getKey(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_InnerStructB_clazz, 0);
+ InnerStructB_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InnerStructB_clazz,
+ env, InnerStructB_clazz(env),
"getKey",
"("
Java_InnerStructB_getValue(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_InnerStructB_clazz, NULL);
+ InnerStructB_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InnerStructB_clazz,
+ env, InnerStructB_clazz(env),
"getValue",
"("
const int kMethodsSampleForTestsSize = arraysize(kMethodsSampleForTests);
- if (env->RegisterNatives(g_SampleForTests_clazz,
+ if (env->RegisterNatives(SampleForTests_clazz(env),
kMethodsSampleForTests,
kMethodsSampleForTestsSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_SampleForTests_clazz, __FILE__);
+ env, SampleForTests_clazz(env), __FILE__);
return false;
}
def GetContent(self):
"""Returns the content of the JNI binding file."""
template = Template("""\
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 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.
template = Template("""\
const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS});
- if (env->RegisterNatives(g_${JAVA_CLASS}_clazz,
+ if (env->RegisterNatives(${JAVA_CLASS}_clazz(env),
kMethods${JAVA_CLASS},
kMethods${JAVA_CLASS}Size) < 0) {
jni_generator::HandleRegistrationError(
- env, g_${JAVA_CLASS}_clazz, __FILE__);
+ env, ${JAVA_CLASS}_clazz(env), __FILE__);
return false;
}
""")
def GetCalledByNativeValues(self, called_by_native):
"""Fills in necessary values for the CalledByNative methods."""
+ java_class = called_by_native.java_class_name or self.class_name
if called_by_native.static or called_by_native.is_constructor:
first_param_in_declaration = ''
- first_param_in_call = ('g_%s_clazz' %
- (called_by_native.java_class_name or
- self.class_name))
+ first_param_in_call = ('%s_clazz(env)' % java_class)
else:
first_param_in_declaration = ', jobject obj'
first_param_in_call = 'obj'
else:
return_clause = 'return ret;'
return {
- 'JAVA_CLASS': called_by_native.java_class_name or self.class_name,
+ 'JAVA_CLASS': java_class,
'RETURN_TYPE': return_type,
'OPTIONAL_ERROR_RETURN': optional_error_return,
'RETURN_DECLARATION': return_declaration,
${FUNCTION_HEADER}
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL},
- g_${JAVA_CLASS}_clazz${OPTIONAL_ERROR_RETURN});
+ ${JAVA_CLASS}_clazz(env)${OPTIONAL_ERROR_RETURN});
jmethodID method_id =
${GET_METHOD_ID_IMPL}
${RETURN_DECLARATION}
}
ret += [template.substitute(values)]
ret += ''
- for clazz in called_by_native_classes:
+
+ class_getter_methods = []
+ if self.options.native_exports:
+ template = Template("""\
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+base::subtle::AtomicWord g_${JAVA_CLASS}_clazz = 0;
+#define ${JAVA_CLASS}_clazz(env) \
+base::android::LazyGetClass(env, k${JAVA_CLASS}ClassPath, \
+&g_${JAVA_CLASS}_clazz)""")
+ else:
template = Template("""\
// Leaking this jclass as we cannot use LazyInstance from some threads.
-jclass g_${JAVA_CLASS}_clazz = NULL;""")
+jclass g_${JAVA_CLASS}_clazz = NULL;
+#define ${JAVA_CLASS}_clazz(env) g_${JAVA_CLASS}_clazz""")
+
+ for clazz in called_by_native_classes:
values = {
'JAVA_CLASS': clazz,
}
ret += [template.substitute(values)]
+
return '\n'.join(ret)
def GetFindClasses(self):
"""Returns the imlementation of FindClass for all known classes."""
if self.init_native:
- template = Template("""\
+ if self.options.native_exports:
+ template = Template("""\
+ base::subtle::Release_Store(&g_${JAVA_CLASS}_clazz,
+ static_cast<base::subtle::AtomicWord>(env->NewWeakGlobalRef(clazz));""")
+ else:
+ template = Template("""\
g_${JAVA_CLASS}_clazz = static_cast<jclass>(env->NewWeakGlobalRef(clazz));""")
else:
+ if self.options.native_exports:
+ return '\n'
template = Template("""\
g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, k${JAVA_CLASS}ClassPath).obj()));""")
if self.options.eager_called_by_natives:
template = Template("""\
env->Get${STATIC_METHOD_PART}MethodID(
- g_${JAVA_CLASS}_clazz,
+ ${JAVA_CLASS}_clazz(env),
"${JNI_NAME}", ${JNI_SIGNATURE});""")
else:
template = Template("""\
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_${STATIC}>(
- env, g_${JAVA_CLASS}_clazz,
+ env, ${JAVA_CLASS}_clazz(env),
"${JNI_NAME}",
${JNI_SIGNATURE},
&g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME});
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kInfoBarClassPath[] = "org/chromium/TestJni$InfoBar";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_TestJni_clazz = NULL;
+#define TestJni_clazz(env) g_TestJni_clazz
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_InfoBar_clazz = NULL;
+#define InfoBar_clazz(env) g_InfoBar_clazz
} // namespace
jobject icon) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"showConfirmInfoBar",
"("
jstring args) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"showAutoLoginInfoBar",
"("
static void Java_InfoBar_dismiss(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_InfoBar_clazz);
+ InfoBar_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InfoBar_clazz,
+ env, InfoBar_clazz(env),
"dismiss",
"("
jstring account,
jstring args) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_TestJni_clazz,
- g_TestJni_clazz, false);
+ CHECK_CLAZZ(env, TestJni_clazz(env),
+ TestJni_clazz(env), false);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"shouldShowAutoLogin",
"("
&g_TestJni_shouldShowAutoLogin);
jboolean ret =
- env->CallStaticBooleanMethod(g_TestJni_clazz,
+ env->CallStaticBooleanMethod(TestJni_clazz(env),
method_id, view, realm, account, args);
jni_generator::CheckException(env);
return ret;
static base::android::ScopedJavaLocalRef<jobject> Java_TestJni_openUrl(JNIEnv*
env, jstring url) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_TestJni_clazz,
- g_TestJni_clazz, NULL);
+ CHECK_CLAZZ(env, TestJni_clazz(env),
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"openUrl",
"("
&g_TestJni_openUrl);
jobject ret =
- env->CallStaticObjectMethod(g_TestJni_clazz,
+ env->CallStaticObjectMethod(TestJni_clazz(env),
method_id, url);
jni_generator::CheckException(env);
return base::android::ScopedJavaLocalRef<jobject>(env, ret);
JniIntWrapper iSecondaryID) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz);
+ TestJni_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"activateHardwareAcceleration",
"("
iParam) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz);
+ TestJni_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"uncheckedCall",
"("
Java_TestJni_returnByteArray(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"returnByteArray",
"("
Java_TestJni_returnBooleanArray(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"returnBooleanArray",
"("
Java_TestJni_returnCharArray(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"returnCharArray",
"("
Java_TestJni_returnShortArray(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"returnShortArray",
"("
Java_TestJni_returnIntArray(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"returnIntArray",
"("
Java_TestJni_returnLongArray(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"returnLongArray",
"("
Java_TestJni_returnDoubleArray(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"returnDoubleArray",
"("
Java_TestJni_returnObjectArray(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"returnObjectArray",
"("
Java_TestJni_returnArrayOfByteArray(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"returnArrayOfByteArray",
"("
Java_TestJni_getCompressFormat(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"getCompressFormat",
"("
Java_TestJni_getCompressFormatList(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_TestJni_clazz, NULL);
+ TestJni_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_TestJni_clazz,
+ env, TestJni_clazz(env),
"getCompressFormatList",
"("
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kMotionEventClassPath[] = "android/view/MotionEvent";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_MotionEvent_clazz = NULL;
+#define MotionEvent_clazz(env) g_MotionEvent_clazz
} // namespace
static void Java_MotionEvent_finalize(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"finalize",
"()V",
&g_MotionEvent_finalize);
JniIntWrapper p12,
JniIntWrapper p13) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_MotionEvent_clazz,
- g_MotionEvent_clazz, NULL);
+ CHECK_CLAZZ(env, MotionEvent_clazz(env),
+ MotionEvent_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"obtain",
"(JJII[Landroid/view/MotionEvent$PointerProperties;[Landroid/view/MotionEvent$PointerCoords;IIFFIIII)Landroid/view/MotionEvent;",
&g_MotionEvent_obtainAVME_J_J_I_I_LAVMEPP_LAVMEPC_I_I_F_F_I_I_I_I);
jobject ret =
- env->CallStaticObjectMethod(g_MotionEvent_clazz,
+ env->CallStaticObjectMethod(MotionEvent_clazz(env),
method_id, p0, p1, as_jint(p2), as_jint(p3), p4, p5, as_jint(p6),
as_jint(p7), p8, p9, as_jint(p10), as_jint(p11), as_jint(p12),
as_jint(p13));
JniIntWrapper p11,
JniIntWrapper p12) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_MotionEvent_clazz,
- g_MotionEvent_clazz, NULL);
+ CHECK_CLAZZ(env, MotionEvent_clazz(env),
+ MotionEvent_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"obtain",
"(JJII[I[Landroid/view/MotionEvent$PointerCoords;IFFIIII)Landroid/view/MotionEvent;",
&g_MotionEvent_obtainAVME_J_J_I_I_AI_LAVMEPC_I_F_F_I_I_I_I);
jobject ret =
- env->CallStaticObjectMethod(g_MotionEvent_clazz,
+ env->CallStaticObjectMethod(MotionEvent_clazz(env),
method_id, p0, p1, as_jint(p2), as_jint(p3), p4, p5, as_jint(p6), p7,
p8, as_jint(p9), as_jint(p10), as_jint(p11), as_jint(p12));
jni_generator::CheckException(env);
JniIntWrapper p10,
JniIntWrapper p11) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_MotionEvent_clazz,
- g_MotionEvent_clazz, NULL);
+ CHECK_CLAZZ(env, MotionEvent_clazz(env),
+ MotionEvent_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"obtain",
"(JJIFFFFIFFII)Landroid/view/MotionEvent;",
&g_MotionEvent_obtainAVME_J_J_I_F_F_F_F_I_F_F_I_I);
jobject ret =
- env->CallStaticObjectMethod(g_MotionEvent_clazz,
+ env->CallStaticObjectMethod(MotionEvent_clazz(env),
method_id, p0, p1, as_jint(p2), p3, p4, p5, p6, as_jint(p7), p8, p9,
as_jint(p10), as_jint(p11));
jni_generator::CheckException(env);
JniIntWrapper p11,
JniIntWrapper p12) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_MotionEvent_clazz,
- g_MotionEvent_clazz, NULL);
+ CHECK_CLAZZ(env, MotionEvent_clazz(env),
+ MotionEvent_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"obtain",
"(JJIIFFFFIFFII)Landroid/view/MotionEvent;",
&g_MotionEvent_obtainAVME_J_J_I_I_F_F_F_F_I_F_F_I_I);
jobject ret =
- env->CallStaticObjectMethod(g_MotionEvent_clazz,
+ env->CallStaticObjectMethod(MotionEvent_clazz(env),
method_id, p0, p1, as_jint(p2), as_jint(p3), p4, p5, p6, p7,
as_jint(p8), p9, p10, as_jint(p11), as_jint(p12));
jni_generator::CheckException(env);
jfloat p4,
JniIntWrapper p5) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_MotionEvent_clazz,
- g_MotionEvent_clazz, NULL);
+ CHECK_CLAZZ(env, MotionEvent_clazz(env),
+ MotionEvent_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"obtain",
"(JJIFFI)Landroid/view/MotionEvent;",
&g_MotionEvent_obtainAVME_J_J_I_F_F_I);
jobject ret =
- env->CallStaticObjectMethod(g_MotionEvent_clazz,
+ env->CallStaticObjectMethod(MotionEvent_clazz(env),
method_id, p0, p1, as_jint(p2), p3, p4, as_jint(p5));
jni_generator::CheckException(env);
return base::android::ScopedJavaLocalRef<jobject>(env, ret);
static base::android::ScopedJavaLocalRef<jobject>
Java_MotionEvent_obtainAVME_AVME(JNIEnv* env, jobject p0) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_MotionEvent_clazz,
- g_MotionEvent_clazz, NULL);
+ CHECK_CLAZZ(env, MotionEvent_clazz(env),
+ MotionEvent_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"obtain",
"(Landroid/view/MotionEvent;)Landroid/view/MotionEvent;",
&g_MotionEvent_obtainAVME_AVME);
jobject ret =
- env->CallStaticObjectMethod(g_MotionEvent_clazz,
+ env->CallStaticObjectMethod(MotionEvent_clazz(env),
method_id, p0);
jni_generator::CheckException(env);
return base::android::ScopedJavaLocalRef<jobject>(env, ret);
static base::android::ScopedJavaLocalRef<jobject>
Java_MotionEvent_obtainNoHistory(JNIEnv* env, jobject p0) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_MotionEvent_clazz,
- g_MotionEvent_clazz, NULL);
+ CHECK_CLAZZ(env, MotionEvent_clazz(env),
+ MotionEvent_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"obtainNoHistory",
"(Landroid/view/MotionEvent;)Landroid/view/MotionEvent;",
&g_MotionEvent_obtainNoHistory);
jobject ret =
- env->CallStaticObjectMethod(g_MotionEvent_clazz,
+ env->CallStaticObjectMethod(MotionEvent_clazz(env),
method_id, p0);
jni_generator::CheckException(env);
return base::android::ScopedJavaLocalRef<jobject>(env, ret);
static void Java_MotionEvent_recycle(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"recycle",
"()V",
&g_MotionEvent_recycle);
static jint Java_MotionEvent_getDeviceId(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getDeviceId",
"()I",
&g_MotionEvent_getDeviceId);
static jint Java_MotionEvent_getSource(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getSource",
"()I",
&g_MotionEvent_getSource);
p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"setSource",
"(I)V",
&g_MotionEvent_setSource);
static jint Java_MotionEvent_getAction(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getAction",
"()I",
&g_MotionEvent_getAction);
static jint Java_MotionEvent_getActionMasked(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getActionMasked",
"()I",
&g_MotionEvent_getActionMasked);
static jint Java_MotionEvent_getActionIndex(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getActionIndex",
"()I",
&g_MotionEvent_getActionIndex);
static jint Java_MotionEvent_getFlags(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getFlags",
"()I",
&g_MotionEvent_getFlags);
static jlong Java_MotionEvent_getDownTime(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getDownTime",
"()J",
&g_MotionEvent_getDownTime);
static jlong Java_MotionEvent_getEventTime(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getEventTime",
"()J",
&g_MotionEvent_getEventTime);
static jfloat Java_MotionEvent_getXF(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getX",
"()F",
&g_MotionEvent_getXF);
static jfloat Java_MotionEvent_getYF(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getY",
"()F",
&g_MotionEvent_getYF);
static jfloat Java_MotionEvent_getPressureF(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getPressure",
"()F",
&g_MotionEvent_getPressureF);
static jfloat Java_MotionEvent_getSizeF(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getSize",
"()F",
&g_MotionEvent_getSizeF);
static jfloat Java_MotionEvent_getTouchMajorF(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getTouchMajor",
"()F",
&g_MotionEvent_getTouchMajorF);
static jfloat Java_MotionEvent_getTouchMinorF(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getTouchMinor",
"()F",
&g_MotionEvent_getTouchMinorF);
static jfloat Java_MotionEvent_getToolMajorF(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getToolMajor",
"()F",
&g_MotionEvent_getToolMajorF);
static jfloat Java_MotionEvent_getToolMinorF(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getToolMinor",
"()F",
&g_MotionEvent_getToolMinorF);
static jfloat Java_MotionEvent_getOrientationF(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getOrientation",
"()F",
&g_MotionEvent_getOrientationF);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getAxisValue",
"(I)F",
&g_MotionEvent_getAxisValueF_I);
static jint Java_MotionEvent_getPointerCount(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getPointerCount",
"()I",
&g_MotionEvent_getPointerCount);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getPointerId",
"(I)I",
&g_MotionEvent_getPointerId);
p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getToolType",
"(I)I",
&g_MotionEvent_getToolType);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"findPointerIndex",
"(I)I",
&g_MotionEvent_findPointerIndex);
p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getX",
"(I)F",
&g_MotionEvent_getXF_I);
p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getY",
"(I)F",
&g_MotionEvent_getYF_I);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getPressure",
"(I)F",
&g_MotionEvent_getPressureF_I);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getSize",
"(I)F",
&g_MotionEvent_getSizeF_I);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getTouchMajor",
"(I)F",
&g_MotionEvent_getTouchMajorF_I);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getTouchMinor",
"(I)F",
&g_MotionEvent_getTouchMinorF_I);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getToolMajor",
"(I)F",
&g_MotionEvent_getToolMajorF_I);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getToolMinor",
"(I)F",
&g_MotionEvent_getToolMinorF_I);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getOrientation",
"(I)F",
&g_MotionEvent_getOrientationF_I);
JniIntWrapper p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getAxisValue",
"(II)F",
&g_MotionEvent_getAxisValueF_I_I);
jobject p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getPointerCoords",
"(ILandroid/view/MotionEvent$PointerCoords;)V",
&g_MotionEvent_getPointerCoords);
jobject p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getPointerProperties",
"(ILandroid/view/MotionEvent$PointerProperties;)V",
&g_MotionEvent_getPointerProperties);
static jint Java_MotionEvent_getMetaState(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getMetaState",
"()I",
&g_MotionEvent_getMetaState);
static jint Java_MotionEvent_getButtonState(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getButtonState",
"()I",
&g_MotionEvent_getButtonState);
static jfloat Java_MotionEvent_getRawX(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getRawX",
"()F",
&g_MotionEvent_getRawX);
static jfloat Java_MotionEvent_getRawY(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getRawY",
"()F",
&g_MotionEvent_getRawY);
static jfloat Java_MotionEvent_getXPrecision(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getXPrecision",
"()F",
&g_MotionEvent_getXPrecision);
static jfloat Java_MotionEvent_getYPrecision(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getYPrecision",
"()F",
&g_MotionEvent_getYPrecision);
static jint Java_MotionEvent_getHistorySize(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistorySize",
"()I",
&g_MotionEvent_getHistorySize);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalEventTime",
"(I)J",
&g_MotionEvent_getHistoricalEventTime);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalX",
"(I)F",
&g_MotionEvent_getHistoricalXF_I);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalY",
"(I)F",
&g_MotionEvent_getHistoricalYF_I);
obj, JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalPressure",
"(I)F",
&g_MotionEvent_getHistoricalPressureF_I);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalSize",
"(I)F",
&g_MotionEvent_getHistoricalSizeF_I);
obj, JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalTouchMajor",
"(I)F",
&g_MotionEvent_getHistoricalTouchMajorF_I);
obj, JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalTouchMinor",
"(I)F",
&g_MotionEvent_getHistoricalTouchMinorF_I);
obj, JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalToolMajor",
"(I)F",
&g_MotionEvent_getHistoricalToolMajorF_I);
obj, JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalToolMinor",
"(I)F",
&g_MotionEvent_getHistoricalToolMinorF_I);
obj, JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalOrientation",
"(I)F",
&g_MotionEvent_getHistoricalOrientationF_I);
JniIntWrapper p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalAxisValue",
"(II)F",
&g_MotionEvent_getHistoricalAxisValueF_I_I);
JniIntWrapper p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalX",
"(II)F",
&g_MotionEvent_getHistoricalXF_I_I);
JniIntWrapper p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalY",
"(II)F",
&g_MotionEvent_getHistoricalYF_I_I);
JniIntWrapper p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalPressure",
"(II)F",
&g_MotionEvent_getHistoricalPressureF_I_I);
JniIntWrapper p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalSize",
"(II)F",
&g_MotionEvent_getHistoricalSizeF_I_I);
JniIntWrapper p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalTouchMajor",
"(II)F",
&g_MotionEvent_getHistoricalTouchMajorF_I_I);
JniIntWrapper p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalTouchMinor",
"(II)F",
&g_MotionEvent_getHistoricalTouchMinorF_I_I);
JniIntWrapper p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalToolMajor",
"(II)F",
&g_MotionEvent_getHistoricalToolMajorF_I_I);
JniIntWrapper p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalToolMinor",
"(II)F",
&g_MotionEvent_getHistoricalToolMinorF_I_I);
JniIntWrapper p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalOrientation",
"(II)F",
&g_MotionEvent_getHistoricalOrientationF_I_I);
JniIntWrapper p2) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalAxisValue",
"(III)F",
&g_MotionEvent_getHistoricalAxisValueF_I_I_I);
jobject p2) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getHistoricalPointerCoords",
"(IILandroid/view/MotionEvent$PointerCoords;)V",
&g_MotionEvent_getHistoricalPointerCoords);
static jint Java_MotionEvent_getEdgeFlags(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, 0);
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"getEdgeFlags",
"()I",
&g_MotionEvent_getEdgeFlags);
JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"setEdgeFlags",
"(I)V",
&g_MotionEvent_setEdgeFlags);
p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"setAction",
"(I)V",
&g_MotionEvent_setAction);
jfloat p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"offsetLocation",
"(FF)V",
&g_MotionEvent_offsetLocation);
jfloat p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"setLocation",
"(FF)V",
&g_MotionEvent_setLocation);
static void Java_MotionEvent_transform(JNIEnv* env, jobject obj, jobject p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"transform",
"(Landroid/graphics/Matrix;)V",
&g_MotionEvent_transform);
JniIntWrapper p5) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"addBatch",
"(JFFFFI)V",
&g_MotionEvent_addBatchV_J_F_F_F_F_I);
JniIntWrapper p2) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"addBatch",
"(J[Landroid/view/MotionEvent$PointerCoords;I)V",
&g_MotionEvent_addBatchV_J_LAVMEPC_I);
Java_MotionEvent_toString(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz, NULL);
+ MotionEvent_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"toString",
"()Ljava/lang/String;",
&g_MotionEvent_toString);
static base::android::ScopedJavaLocalRef<jstring>
Java_MotionEvent_actionToString(JNIEnv* env, JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_MotionEvent_clazz,
- g_MotionEvent_clazz, NULL);
+ CHECK_CLAZZ(env, MotionEvent_clazz(env),
+ MotionEvent_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"actionToString",
"(I)Ljava/lang/String;",
&g_MotionEvent_actionToString);
jstring ret =
- static_cast<jstring>(env->CallStaticObjectMethod(g_MotionEvent_clazz,
+ static_cast<jstring>(env->CallStaticObjectMethod(MotionEvent_clazz(env),
method_id, as_jint(p0)));
jni_generator::CheckException(env);
return base::android::ScopedJavaLocalRef<jstring>(env, ret);
static base::android::ScopedJavaLocalRef<jstring>
Java_MotionEvent_axisToString(JNIEnv* env, JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_MotionEvent_clazz,
- g_MotionEvent_clazz, NULL);
+ CHECK_CLAZZ(env, MotionEvent_clazz(env),
+ MotionEvent_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"axisToString",
"(I)Ljava/lang/String;",
&g_MotionEvent_axisToString);
jstring ret =
- static_cast<jstring>(env->CallStaticObjectMethod(g_MotionEvent_clazz,
+ static_cast<jstring>(env->CallStaticObjectMethod(MotionEvent_clazz(env),
method_id, as_jint(p0)));
jni_generator::CheckException(env);
return base::android::ScopedJavaLocalRef<jstring>(env, ret);
__attribute__ ((unused));
static jint Java_MotionEvent_axisFromString(JNIEnv* env, jstring p0) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_MotionEvent_clazz,
- g_MotionEvent_clazz, 0);
+ CHECK_CLAZZ(env, MotionEvent_clazz(env),
+ MotionEvent_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"axisFromString",
"(Ljava/lang/String;)I",
&g_MotionEvent_axisFromString);
jint ret =
- env->CallStaticIntMethod(g_MotionEvent_clazz,
+ env->CallStaticIntMethod(MotionEvent_clazz(env),
method_id, p0);
jni_generator::CheckException(env);
return ret;
JniIntWrapper p1) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_MotionEvent_clazz);
+ MotionEvent_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_MotionEvent_clazz,
+ env, MotionEvent_clazz(env),
"writeToParcel",
"(Landroid/os/Parcel;I)V",
&g_MotionEvent_writeToParcel);
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kTestClassPath[] = "org/chromium/example/jni_generator/Test";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_Test_clazz = NULL;
+#define Test_clazz(env) g_Test_clazz
jmethodID g_Test_testMethodWithParam = NULL;
jmethodID g_Test_testStaticMethodWithParam = NULL;
jmethodID g_Test_testMethodWithNoParam = NULL;
}
static jint testStaticMethodWithParam(JNIEnv* env, JniIntWrapper iParam) {
- jint ret = env->CallStaticIntMethod(g_Test_clazz,
+ jint ret = env->CallStaticIntMethod(Test_clazz(env),
g_Test_testStaticMethodWithParam, as_jint(iParam));
return ret;
}
static jdouble testMethodWithNoParam(JNIEnv* env) {
- jdouble ret = env->CallStaticDoubleMethod(g_Test_clazz,
+ jdouble ret = env->CallStaticDoubleMethod(Test_clazz(env),
g_Test_testMethodWithNoParam);
return ret;
}
static base::android::ScopedJavaLocalRef<jstring>
testStaticMethodWithNoParam(JNIEnv* env) {
- jstring ret = static_cast<jstring>(env->CallStaticObjectMethod(g_Test_clazz,
+ jstring ret =
+ static_cast<jstring>(env->CallStaticObjectMethod(Test_clazz(env),
g_Test_testStaticMethodWithNoParam));
return base::android::ScopedJavaLocalRef<jstring>(env, ret);
}
const int kMethodsTestSize = arraysize(kMethodsTest);
- if (env->RegisterNatives(g_Test_clazz,
+ if (env->RegisterNatives(Test_clazz(env),
kMethodsTest,
kMethodsTestSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_Test_clazz, __FILE__);
+ env, Test_clazz(env), __FILE__);
return false;
}
g_Test_testMethodWithParam = env->GetMethodID(
- g_Test_clazz,
+ Test_clazz(env),
"testMethodWithParam",
"("
"I"
}
g_Test_testStaticMethodWithParam = env->GetStaticMethodID(
- g_Test_clazz,
+ Test_clazz(env),
"testStaticMethodWithParam",
"("
"I"
}
g_Test_testMethodWithNoParam = env->GetStaticMethodID(
- g_Test_clazz,
+ Test_clazz(env),
"testMethodWithNoParam",
"("
")"
}
g_Test_testStaticMethodWithNoParam = env->GetStaticMethodID(
- g_Test_clazz,
+ Test_clazz(env),
"testStaticMethodWithNoParam",
"("
")"
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kInputStreamClassPath[] = "java/io/InputStream";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_InputStream_clazz = NULL;
+#define InputStream_clazz(env) g_InputStream_clazz
} // namespace
static jint Java_InputStream_available(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_InputStream_clazz, 0);
+ InputStream_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InputStream_clazz,
+ env, InputStream_clazz(env),
"available",
"()I",
&g_InputStream_available);
static void Java_InputStream_close(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_InputStream_clazz);
+ InputStream_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InputStream_clazz,
+ env, InputStream_clazz(env),
"close",
"()V",
&g_InputStream_close);
static void Java_InputStream_mark(JNIEnv* env, jobject obj, JniIntWrapper p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_InputStream_clazz);
+ InputStream_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InputStream_clazz,
+ env, InputStream_clazz(env),
"mark",
"(I)V",
&g_InputStream_mark);
static jboolean Java_InputStream_markSupported(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_InputStream_clazz, false);
+ InputStream_clazz(env), false);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InputStream_clazz,
+ env, InputStream_clazz(env),
"markSupported",
"()Z",
&g_InputStream_markSupported);
static jint Java_InputStream_readI(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_InputStream_clazz, 0);
+ InputStream_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InputStream_clazz,
+ env, InputStream_clazz(env),
"read",
"()I",
&g_InputStream_readI);
static jint Java_InputStream_readI_AB(JNIEnv* env, jobject obj, jbyteArray p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_InputStream_clazz, 0);
+ InputStream_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InputStream_clazz,
+ env, InputStream_clazz(env),
"read",
"([B)I",
&g_InputStream_readI_AB);
JniIntWrapper p2) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_InputStream_clazz, 0);
+ InputStream_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InputStream_clazz,
+ env, InputStream_clazz(env),
"read",
"([BII)I",
&g_InputStream_readI_AB_I_I);
static void Java_InputStream_reset(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_InputStream_clazz);
+ InputStream_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InputStream_clazz,
+ env, InputStream_clazz(env),
"reset",
"()V",
&g_InputStream_reset);
static jlong Java_InputStream_skip(JNIEnv* env, jobject obj, jlong p0) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_InputStream_clazz, 0);
+ InputStream_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InputStream_clazz,
+ env, InputStream_clazz(env),
"skip",
"(J)J",
&g_InputStream_skip);
static base::android::ScopedJavaLocalRef<jobject>
Java_InputStream_Constructor(JNIEnv* env) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_InputStream_clazz,
- g_InputStream_clazz, NULL);
+ CHECK_CLAZZ(env, InputStream_clazz(env),
+ InputStream_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_InputStream_clazz,
+ env, InputStream_clazz(env),
"<init>",
"()V",
&g_InputStream_Constructor);
jobject ret =
- env->NewObject(g_InputStream_clazz,
+ env->NewObject(InputStream_clazz(env),
method_id);
jni_generator::CheckException(env);
return base::android::ScopedJavaLocalRef<jobject>(env, ret);
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kHashSetClassPath[] = "java/util/HashSet";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_HashSet_clazz = NULL;
+#define HashSet_clazz(env) g_HashSet_clazz
} // namespace
static void Java_HashSet_dummy(JNIEnv* env, jobject obj) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_HashSet_clazz);
+ HashSet_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_HashSet_clazz,
+ env, HashSet_clazz(env),
"dummy",
"()V",
&g_HashSet_dummy);
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kMyInnerClassClassPath[] = "org/chromium/TestJni$MyInnerClass";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_TestJni_clazz = NULL;
+#define TestJni_clazz(env) g_TestJni_clazz
} // namespace
const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
- if (env->RegisterNatives(g_MyInnerClass_clazz,
+ if (env->RegisterNatives(MyInnerClass_clazz(env),
kMethodsMyInnerClass,
kMethodsMyInnerClassSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_MyInnerClass_clazz, __FILE__);
+ env, MyInnerClass_clazz(env), __FILE__);
return false;
}
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kTestJniClassPath[] = "org/chromium/TestJni";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_TestJni_clazz = NULL;
+#define TestJni_clazz(env) g_TestJni_clazz
} // namespace
const int kMethodsMyOtherInnerClassSize =
arraysize(kMethodsMyOtherInnerClass);
- if (env->RegisterNatives(g_MyOtherInnerClass_clazz,
+ if (env->RegisterNatives(MyOtherInnerClass_clazz(env),
kMethodsMyOtherInnerClass,
kMethodsMyOtherInnerClassSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_MyOtherInnerClass_clazz, __FILE__);
+ env, MyOtherInnerClass_clazz(env), __FILE__);
return false;
}
const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
- if (env->RegisterNatives(g_TestJni_clazz,
+ if (env->RegisterNatives(TestJni_clazz(env),
kMethodsTestJni,
kMethodsTestJniSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_TestJni_clazz, __FILE__);
+ env, TestJni_clazz(env), __FILE__);
return false;
}
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kMyInnerClassClassPath[] = "org/chromium/TestJni$MyInnerClass";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_TestJni_clazz = NULL;
+#define TestJni_clazz(env) g_TestJni_clazz
} // namespace
const int kMethodsMyOtherInnerClassSize =
arraysize(kMethodsMyOtherInnerClass);
- if (env->RegisterNatives(g_MyOtherInnerClass_clazz,
+ if (env->RegisterNatives(MyOtherInnerClass_clazz(env),
kMethodsMyOtherInnerClass,
kMethodsMyOtherInnerClassSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_MyOtherInnerClass_clazz, __FILE__);
+ env, MyOtherInnerClass_clazz(env), __FILE__);
return false;
}
const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
- if (env->RegisterNatives(g_MyInnerClass_clazz,
+ if (env->RegisterNatives(MyInnerClass_clazz(env),
kMethodsMyInnerClass,
kMethodsMyInnerClassSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_MyInnerClass_clazz, __FILE__);
+ env, MyInnerClass_clazz(env), __FILE__);
return false;
}
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kTestClassPath[] = "org/chromium/example/jni_generator/Test";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_Test_clazz = NULL;
+#define Test_clazz(env) g_Test_clazz
} // namespace
const int kMethodsTestSize = arraysize(kMethodsTest);
- if (env->RegisterNatives(g_Test_clazz,
+ if (env->RegisterNatives(Test_clazz(env),
kMethodsTest,
kMethodsTestSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_Test_clazz, __FILE__);
+ env, Test_clazz(env), __FILE__);
return false;
}
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kExampleClassPath[] = "com/test/jni_generator/Example";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_Example_clazz = NULL;
+#define Example_clazz(env) g_Example_clazz
} // namespace
const int kMethodsExampleSize = arraysize(kMethodsExample);
- if (env->RegisterNatives(g_Example_clazz,
+ if (env->RegisterNatives(Example_clazz(env),
kMethodsExample,
kMethodsExampleSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_Example_clazz, __FILE__);
+ env, Example_clazz(env), __FILE__);
return false;
}
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kFooClassPath[] = "org/chromium/foo/Foo";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_Foo_clazz = NULL;
+#define Foo_clazz(env) g_Foo_clazz
} // namespace
static void Java_Foo_calledByNative(JNIEnv* env, jobject callback1,
jobject callback2) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_Foo_clazz,
- g_Foo_clazz);
+ CHECK_CLAZZ(env, Foo_clazz(env),
+ Foo_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_Foo_clazz,
+ env, Foo_clazz(env),
"calledByNative",
"("
"V",
&g_Foo_calledByNative);
- env->CallStaticVoidMethod(g_Foo_clazz,
+ env->CallStaticVoidMethod(Foo_clazz(env),
method_id, callback1, callback2);
jni_generator::CheckException(env);
const int kMethodsFooSize = arraysize(kMethodsFoo);
- if (env->RegisterNatives(g_Foo_clazz,
+ if (env->RegisterNatives(Foo_clazz(env),
kMethodsFoo,
kMethodsFooSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_Foo_clazz, __FILE__);
+ env, Foo_clazz(env), __FILE__);
return false;
}
const char kSampleForTestsClassPath[] =
"org/chromium/example/jni_generator/SampleForTests";
// Leaking this jclass as we cannot use LazyInstance from some threads.
-jclass g_SampleForTests_clazz = NULL;
+base::subtle::AtomicWord g_SampleForTests_clazz = 0;
+#define SampleForTests_clazz(env) base::android::LazyGetClass(env, kSampleForTestsClassPath, &g_SampleForTests_clazz)
} // namespace
JniIntWrapper iParam) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_SampleForTests_clazz);
+ SampleForTests_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_SampleForTests_clazz,
+ env, SampleForTests_clazz(env),
"testMethodWithParam",
"("
JniIntWrapper iParam) {
/* Must call RegisterNativesImpl() */
CHECK_CLAZZ(env, obj,
- g_SampleForTests_clazz, NULL);
+ SampleForTests_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_INSTANCE>(
- env, g_SampleForTests_clazz,
+ env, SampleForTests_clazz(env),
"testMethodWithParamAndReturn",
"("
static jint Java_SampleForTests_testStaticMethodWithParam(JNIEnv* env,
JniIntWrapper iParam) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_SampleForTests_clazz,
- g_SampleForTests_clazz, 0);
+ CHECK_CLAZZ(env, SampleForTests_clazz(env),
+ SampleForTests_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_SampleForTests_clazz,
+ env, SampleForTests_clazz(env),
"testStaticMethodWithParam",
"("
&g_SampleForTests_testStaticMethodWithParam);
jint ret =
- env->CallStaticIntMethod(g_SampleForTests_clazz,
+ env->CallStaticIntMethod(SampleForTests_clazz(env),
method_id, as_jint(iParam));
jni_generator::CheckException(env);
return ret;
static base::subtle::AtomicWord g_SampleForTests_testMethodWithNoParam = 0;
static jdouble Java_SampleForTests_testMethodWithNoParam(JNIEnv* env) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_SampleForTests_clazz,
- g_SampleForTests_clazz, 0);
+ CHECK_CLAZZ(env, SampleForTests_clazz(env),
+ SampleForTests_clazz(env), 0);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_SampleForTests_clazz,
+ env, SampleForTests_clazz(env),
"testMethodWithNoParam",
"("
&g_SampleForTests_testMethodWithNoParam);
jdouble ret =
- env->CallStaticDoubleMethod(g_SampleForTests_clazz,
+ env->CallStaticDoubleMethod(SampleForTests_clazz(env),
method_id);
jni_generator::CheckException(env);
return ret;
static base::android::ScopedJavaLocalRef<jstring>
Java_SampleForTests_testStaticMethodWithNoParam(JNIEnv* env) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_SampleForTests_clazz,
- g_SampleForTests_clazz, NULL);
+ CHECK_CLAZZ(env, SampleForTests_clazz(env),
+ SampleForTests_clazz(env), NULL);
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_SampleForTests_clazz,
+ env, SampleForTests_clazz(env),
"testStaticMethodWithNoParam",
"("
&g_SampleForTests_testStaticMethodWithNoParam);
jstring ret =
- static_cast<jstring>(env->CallStaticObjectMethod(g_SampleForTests_clazz,
+static_cast<jstring>(env->CallStaticObjectMethod(SampleForTests_clazz(env),
method_id));
jni_generator::CheckException(env);
return base::android::ScopedJavaLocalRef<jstring>(env, ret);
// Step 3: RegisterNatives.
static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) {
- g_SampleForTests_clazz = static_cast<jclass>(env->NewWeakGlobalRef(clazz));
+ base::subtle::Release_Store(&g_SampleForTests_clazz,
+ static_cast<base::subtle::AtomicWord>(env->NewWeakGlobalRef(clazz));
return true;
}
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kTestJniClassPath[] = "org/chromium/TestJni";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_TestJni_clazz = NULL;
+#define TestJni_clazz(env) g_TestJni_clazz
} // namespace
const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
- if (env->RegisterNatives(g_TestJni_clazz,
+ if (env->RegisterNatives(TestJni_clazz(env),
kMethodsTestJni,
kMethodsTestJniSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_TestJni_clazz, __FILE__);
+ env, TestJni_clazz(env), __FILE__);
return false;
}
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kTestJniClassPath[] = "org/chromium/TestJni";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_TestJni_clazz = NULL;
+#define TestJni_clazz(env) g_TestJni_clazz
} // namespace
const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
- if (env->RegisterNatives(g_TestJni_clazz,
+ if (env->RegisterNatives(TestJni_clazz(env),
kMethodsTestJni,
kMethodsTestJniSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_TestJni_clazz, __FILE__);
+ env, TestJni_clazz(env), __FILE__);
return false;
}
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kTestClassPath[] = "org/chromium/example/jni_generator/Test";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_Test_clazz = NULL;
+#define Test_clazz(env) g_Test_clazz
} // namespace
const int kMethodsTestSize = arraysize(kMethodsTest);
- if (env->RegisterNatives(g_Test_clazz,
+ if (env->RegisterNatives(Test_clazz(env),
kMethodsTest,
kMethodsTestSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_Test_clazz, __FILE__);
+ env, Test_clazz(env), __FILE__);
return false;
}
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const char kFooClassPath[] = "org/chromium/foo/Foo";
// Leaking this jclass as we cannot use LazyInstance from some threads.
jclass g_Foo_clazz = NULL;
+#define Foo_clazz(env) g_Foo_clazz
} // namespace
static base::subtle::AtomicWord g_Foo_calledByNative = 0;
static void Java_Foo_calledByNative(JNIEnv* env, jobject callback) {
/* Must call RegisterNativesImpl() */
- CHECK_CLAZZ(env, g_Foo_clazz,
- g_Foo_clazz);
+ CHECK_CLAZZ(env, Foo_clazz(env),
+ Foo_clazz(env));
jmethodID method_id =
base::android::MethodID::LazyGet<
base::android::MethodID::TYPE_STATIC>(
- env, g_Foo_clazz,
+ env, Foo_clazz(env),
"calledByNative",
"("
"V",
&g_Foo_calledByNative);
- env->CallStaticVoidMethod(g_Foo_clazz,
+ env->CallStaticVoidMethod(Foo_clazz(env),
method_id, callback);
jni_generator::CheckException(env);
const int kMethodsFooSize = arraysize(kMethodsFoo);
- if (env->RegisterNatives(g_Foo_clazz,
+ if (env->RegisterNatives(Foo_clazz(env),
kMethodsFoo,
kMethodsFooSize) < 0) {
jni_generator::HandleRegistrationError(
- env, g_Foo_clazz, __FILE__);
+ env, Foo_clazz(env), __FILE__);
return false;
}
--- /dev/null
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_utils.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+
+#include "jni/JNIUtils_jni.h"
+
+namespace base {
+namespace android {
+
+ScopedJavaLocalRef<jobject> GetClassLoader(JNIEnv* env) {
+ return Java_JNIUtils_getClassLoader(env);
+}
+
+bool RegisterJNIUtils(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
+
--- /dev/null
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_ANDROID_JNI_UTILS_H_
+#define BASE_ANDROID_JNI_UTILS_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+
+namespace base {
+
+namespace android {
+
+// Gets a ClassLoader instance capable of loading Chromium java classes.
+// This should be called either from JNI_OnLoad or from within a method called
+// via JNI from Java.
+BASE_EXPORT ScopedJavaLocalRef<jobject> GetClassLoader(JNIEnv* env);
+
+bool RegisterJNIUtils(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_JNI_UTILS_H_
+
LOG_INFO("%s: Calling back to java with handler %p, opaque %p",
__FUNCTION__, callback->handler, callback->opaque);
- jlong arg = static_cast<jlong>(reinterpret_cast<intptr_t>(callback));
+ jlong arg = static_cast<jlong>(reinterpret_cast<uintptr_t>(callback));
env->CallStaticVoidMethod(
s_java_callback_bindings.clazz, s_java_callback_bindings.method_id, arg);
'android/java/src/org/chromium/base/EventLog.java',
'android/java/src/org/chromium/base/FieldTrialList.java',
'android/java/src/org/chromium/base/ImportantFileWriterAndroid.java',
+ 'android/java/src/org/chromium/base/JNIUtils.java',
'android/java/src/org/chromium/base/library_loader/LibraryLoader.java',
'android/java/src/org/chromium/base/MemoryPressureListener.java',
'android/java/src/org/chromium/base/JavaHandlerThread.java',
'android/jni_registrar.h',
'android/jni_string.cc',
'android/jni_string.h',
+ 'android/jni_utils.cc',
+ 'android/jni_utils.h',
'android/jni_weak_ref.cc',
'android/jni_weak_ref.h',
'android/library_loader/library_loader_hooks.cc',
#endif // MAC_OS_X_VERSION_10_9
+#if !defined(MAC_OS_X_VERSION_10_10) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10
+
+@interface NSUserActivity : NSObject
+
+@property (readonly, copy) NSString* activityType;
+@property (copy) NSURL* webPageURL;
+
+@end
+
+BASE_EXPORT extern "C" NSString* const NSUserActivityTypeBrowsingWeb;
+
+#endif // MAC_OS_X_VERSION_10_10
+
#endif // BASE_MAC_SDK_FORWARD_DECLARATIONS_H_
@"NSWindowWillEnterFullScreenNotification";
#endif // MAC_OS_X_VERSION_10_7
+
+// Replicate specific 10.10 SDK declarations for building with prior SDKs.
+#if !defined(MAC_OS_X_VERSION_10_10) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10
+
+NSString* const NSUserActivityTypeBrowsingWeb =
+ @"NSUserActivityTypeBrowsingWeb";
+
+#endif // MAC_OS_X_VERSION_10_10
#include "base/location.h"
#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
namespace base {
namespace internal {
IncomingTaskQueue::IncomingTaskQueue(MessageLoop* message_loop)
- : message_loop_(message_loop),
+ : high_res_task_count_(0),
+ message_loop_(message_loop),
next_sequence_num_(0) {
}
AutoLock locked(incoming_queue_lock_);
PendingTask pending_task(
from_here, task, CalculateDelayedRuntime(delay), nestable);
+#if defined(OS_WIN)
+ // We consider the task needs a high resolution timer if the delay is
+ // more than 0 and less than 32ms. This caps the relative error to
+ // less than 50% : a 33ms wait can wake at 48ms since the default
+ // resolution on Windows is between 10 and 15ms.
+ if (delay > TimeDelta() &&
+ delay.InMilliseconds() < (2 * Time::kMinLowResolutionThresholdMs)) {
+ ++high_res_task_count_;
+ pending_task.is_high_res = true;
+ }
+#endif
return PostPendingTask(&pending_task);
}
-bool IncomingTaskQueue::IsHighResolutionTimerEnabledForTesting() {
-#if defined(OS_WIN)
- return !high_resolution_timer_expiration_.is_null();
-#else
- return true;
-#endif
+bool IncomingTaskQueue::HasHighResolutionTasks() {
+ AutoLock lock(incoming_queue_lock_);
+ return high_res_task_count_ > 0;
}
bool IncomingTaskQueue::IsIdleForTesting() {
return incoming_queue_.empty();
}
-void IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) {
+int IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) {
// Make sure no tasks are lost.
DCHECK(work_queue->empty());
// Acquire all we can from the inter-thread queue with one lock acquisition.
AutoLock lock(incoming_queue_lock_);
if (!incoming_queue_.empty())
- incoming_queue_.Swap(work_queue); // Constant time
+ incoming_queue_.Swap(work_queue);
- DCHECK(incoming_queue_.empty());
+ // Reset the count of high resolution tasks since our queue is now empty.
+ int high_res_tasks = high_res_task_count_;
+ high_res_task_count_ = 0;
+ return high_res_tasks;
}
void IncomingTaskQueue::WillDestroyCurrentMessageLoop() {
-#if defined(OS_WIN)
- // If we left the high-resolution timer activated, deactivate it now.
- // Doing this is not-critical, it is mainly to make sure we track
- // the high resolution timer activations properly in our unit tests.
- if (!high_resolution_timer_expiration_.is_null()) {
- Time::ActivateHighResolutionTimer(false);
- high_resolution_timer_expiration_ = TimeTicks();
- }
-#endif
-
AutoLock lock(incoming_queue_lock_);
message_loop_ = NULL;
}
TimeTicks IncomingTaskQueue::CalculateDelayedRuntime(TimeDelta delay) {
TimeTicks delayed_run_time;
- if (delay > TimeDelta()) {
+ if (delay > TimeDelta())
delayed_run_time = TimeTicks::Now() + delay;
-
-#if defined(OS_WIN)
- if (high_resolution_timer_expiration_.is_null()) {
- // Windows timers are granular to 15.6ms. If we only set high-res
- // timers for those under 15.6ms, then a 18ms timer ticks at ~32ms,
- // which as a percentage is pretty inaccurate. So enable high
- // res timers for any timer which is within 2x of the granularity.
- // This is a tradeoff between accuracy and power management.
- bool needs_high_res_timers = delay.InMilliseconds() <
- (2 * Time::kMinLowResolutionThresholdMs);
- if (needs_high_res_timers) {
- if (Time::ActivateHighResolutionTimer(true)) {
- high_resolution_timer_expiration_ = TimeTicks::Now() +
- TimeDelta::FromMilliseconds(
- MessageLoop::kHighResolutionTimerModeLeaseTimeMs);
- }
- }
- }
-#endif
- } else {
+ else
DCHECK_EQ(delay.InMilliseconds(), 0) << "delay should not be negative";
- }
-
-#if defined(OS_WIN)
- if (!high_resolution_timer_expiration_.is_null()) {
- if (TimeTicks::Now() > high_resolution_timer_expiration_) {
- Time::ActivateHighResolutionTimer(false);
- high_resolution_timer_expiration_ = TimeTicks();
- }
- }
-#endif
-
return delayed_run_time;
}
TimeDelta delay,
bool nestable);
- // Returns true if the message loop has high resolution timers enabled.
- // Provided for testing.
- bool IsHighResolutionTimerEnabledForTesting();
+ // Returns true if the queue contains tasks that require higher than default
+ // timer resolution. Currently only needed for Windows.
+ bool HasHighResolutionTasks();
// Returns true if the message loop is "idle". Provided for testing.
bool IsIdleForTesting();
// Loads tasks from the |incoming_queue_| into |*work_queue|. Must be called
- // from the thread that is running the loop.
- void ReloadWorkQueue(TaskQueue* work_queue);
+ // from the thread that is running the loop. Returns the number of tasks that
+ // require high resolution timers.
+ int ReloadWorkQueue(TaskQueue* work_queue);
// Disconnects |this| from the parent message loop.
void WillDestroyCurrentMessageLoop();
// does not retain |pending_task->task| beyond this function call.
bool PostPendingTask(PendingTask* pending_task);
-#if defined(OS_WIN)
- TimeTicks high_resolution_timer_expiration_;
-#endif
+ // Number of tasks that require high resolution timing. This value is kept
+ // so that ReloadWorkQueue() completes in constant time.
+ int high_res_task_count_;
- // The lock that protects access to |incoming_queue_|, |message_loop_| and
- // |next_sequence_num_|.
+ // The lock that protects access to the members of this class.
base::Lock incoming_queue_lock_;
// An incoming queue of tasks that are acquired under a mutex for processing
MessageLoop::MessageLoop(Type type)
: type_(type),
+ pending_high_res_tasks_(0),
+ in_high_res_mode_(false),
nestable_tasks_allowed_(true),
#if defined(OS_WIN)
os_modal_loop_(false),
MessageLoop::MessageLoop(scoped_ptr<MessagePump> pump)
: pump_(pump.Pass()),
type_(TYPE_CUSTOM),
+ pending_high_res_tasks_(0),
+ in_high_res_mode_(false),
nestable_tasks_allowed_(true),
#if defined(OS_WIN)
os_modal_loop_(false),
DCHECK_EQ(this, current());
DCHECK(!run_loop_);
-
+#if defined(OS_WIN)
+ if (in_high_res_mode_)
+ Time::ActivateHighResolutionTimer(false);
+#endif
// Clean up any unprocessed tasks, but take care: deleting a task could
// result in the addition of more tasks (e.g., via DeleteSoon). We set a
// limit on the number of times we will allow a deleted task to generate more
return run_loop_ != NULL;
}
-bool MessageLoop::IsHighResolutionTimerEnabledForTesting() {
- return incoming_task_queue_->IsHighResolutionTimerEnabledForTesting();
+bool MessageLoop::HasHighResolutionTasks() {
+ return incoming_task_queue_->HasHighResolutionTasks();
}
bool MessageLoop::IsIdleForTesting() {
void MessageLoop::RunTask(const PendingTask& pending_task) {
DCHECK(nestable_tasks_allowed_);
+ if (pending_task.is_high_res) {
+ pending_high_res_tasks_--;
+ CHECK(pending_high_res_tasks_ >= 0);
+ }
// Execute the task and assume the worst: It is probably not reentrant.
nestable_tasks_allowed_ = false;
// |*work_queue| by waiting until the last minute (|*work_queue| is empty) to
// load. That reduces the number of locks-per-task significantly when our
// queues get large.
- if (work_queue_.empty())
- incoming_task_queue_->ReloadWorkQueue(&work_queue_);
+ if (work_queue_.empty()) {
+ pending_high_res_tasks_ +=
+ incoming_task_queue_->ReloadWorkQueue(&work_queue_);
+ }
}
void MessageLoop::ScheduleWork(bool was_empty) {
if (run_loop_->quit_when_idle_received_)
pump_->Quit();
+ // When we return we will do a kernel wait for more tasks.
+#if defined(OS_WIN)
+ // On Windows we activate the high resolution timer so that the wait
+ // _if_ triggered by the timer happens with good resolution. If we don't
+ // do this the default resolution is 15ms which might not be acceptable
+ // for some tasks.
+ bool high_res = pending_high_res_tasks_ > 0;
+ if (high_res != in_high_res_mode_) {
+ in_high_res_mode_ = high_res;
+ Time::ActivateHighResolutionTimer(in_high_res_mode_);
+ }
+#endif
return false;
}
void AddTaskObserver(TaskObserver* task_observer);
void RemoveTaskObserver(TaskObserver* task_observer);
- // When we go into high resolution timer mode, we will stay in hi-res mode
- // for at least 1s.
- static const int kHighResolutionTimerModeLeaseTimeMs = 1000;
-
#if defined(OS_WIN)
void set_os_modal_loop(bool os_modal_loop) {
os_modal_loop_ = os_modal_loop;
// Returns true if the message loop has high resolution timers enabled.
// Provided for testing.
- bool IsHighResolutionTimerEnabledForTesting();
+ bool HasHighResolutionTasks();
// Returns true if the message loop is "idle". Provided for testing.
bool IsIdleForTesting();
// this queue is only accessed (push/pop) by our current thread.
TaskQueue work_queue_;
+ // How many high resolution tasks are in the pending task queue. This value
+ // increases by N every time we call ReloadWorkQueue() and decreases by 1
+ // every time we call RunTask() if the task needs a high resolution timer.
+ int pending_high_res_tasks_;
+ // Tracks if we have requested high resolution timers. Its only use is to
+ // turn off the high resolution timer upon loop destruction.
+ bool in_high_res_mode_;
+
// Contains delayed tasks, sorted by their 'delayed_run_time' property.
DelayedTaskQueue delayed_work_queue_;
TEST(MessageLoopTest, HighResolutionTimer) {
MessageLoop loop;
+ Time::EnableHighResolutionTimer(true);
const TimeDelta kFastTimer = TimeDelta::FromMilliseconds(5);
const TimeDelta kSlowTimer = TimeDelta::FromMilliseconds(100);
- EXPECT_FALSE(loop.IsHighResolutionTimerEnabledForTesting());
-
+ EXPECT_FALSE(loop.HasHighResolutionTasks());
// Post a fast task to enable the high resolution timers.
loop.PostDelayedTask(FROM_HERE, Bind(&PostNTasksThenQuit, 1),
kFastTimer);
+ EXPECT_TRUE(loop.HasHighResolutionTasks());
loop.Run();
- EXPECT_TRUE(loop.IsHighResolutionTimerEnabledForTesting());
-
- // Post a slow task and verify high resolution timers
- // are still enabled.
- loop.PostDelayedTask(FROM_HERE, Bind(&PostNTasksThenQuit, 1),
- kSlowTimer);
- loop.Run();
- EXPECT_TRUE(loop.IsHighResolutionTimerEnabledForTesting());
-
- // Wait for a while so that high-resolution mode elapses.
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(
- MessageLoop::kHighResolutionTimerModeLeaseTimeMs));
-
- // Post a slow task to disable the high resolution timers.
+ EXPECT_FALSE(loop.HasHighResolutionTasks());
+ EXPECT_FALSE(Time::IsHighResolutionTimerInUse());
+ // Check that a slow task does not trigger the high resolution logic.
loop.PostDelayedTask(FROM_HERE, Bind(&PostNTasksThenQuit, 1),
kSlowTimer);
+ EXPECT_FALSE(loop.HasHighResolutionTasks());
loop.Run();
- EXPECT_FALSE(loop.IsHighResolutionTimerEnabledForTesting());
+ EXPECT_FALSE(loop.HasHighResolutionTasks());
+ Time::EnableHighResolutionTimer(false);
}
#endif // defined(OS_WIN)
virtual bool DoDelayedWork(TimeTicks* next_delayed_work_time) = 0;
// Called from within Run just before the message pump goes to sleep.
- // Returns true to indicate that idle work was done.
+ // Returns true to indicate that idle work was done. Returning false means
+ // the pump will now wait.
virtual bool DoIdleWork() = 0;
};
namespace base {
-#if _MSC_VER >= 1700
-// This a temporary fix for compiling on VS2012. http://crbug.com/154744
-PendingTask::PendingTask() : sequence_num(-1), nestable(false) {
-}
-#endif
-
PendingTask::PendingTask(const tracked_objects::Location& posted_from,
const base::Closure& task)
: base::TrackingInfo(posted_from, TimeTicks()),
task(task),
posted_from(posted_from),
sequence_num(0),
- nestable(true) {
+ nestable(true),
+ is_high_res(false) {
}
PendingTask::PendingTask(const tracked_objects::Location& posted_from,
task(task),
posted_from(posted_from),
sequence_num(0),
- nestable(nestable) {
+ nestable(nestable),
+ is_high_res(false) {
}
PendingTask::~PendingTask() {
// Contains data about a pending task. Stored in TaskQueue and DelayedTaskQueue
// for use by classes that queue and execute tasks.
struct BASE_EXPORT PendingTask : public TrackingInfo {
-#if _MSC_VER >= 1700
- PendingTask();
-#endif
PendingTask(const tracked_objects::Location& posted_from,
const Closure& task);
PendingTask(const tracked_objects::Location& posted_from,
// OK to dispatch from a nested loop.
bool nestable;
+
+ // Needs high resolution timers.
+ bool is_high_res;
};
// Wrapper around std::queue specialized for PendingTask which adds a Swap
// treat it as static across all windows versions.
static const int kMinLowResolutionThresholdMs = 16;
- // Enable or disable Windows high resolution timer. If the high resolution
- // timer is not enabled, calls to ActivateHighResolutionTimer will fail.
- // When disabling the high resolution timer, this function will not cause
- // the high resolution timer to be deactivated, but will prevent future
- // activations.
- // Must be called from the main thread.
- // For more details see comments in time_win.cc.
+ // Enable or disable Windows high resolution timer.
static void EnableHighResolutionTimer(bool enable);
// Activates or deactivates the high resolution timer based on the |activate|
// platform-dependent epoch.
static const int64 kTimeTToMicrosecondsOffset;
-#if defined(OS_WIN)
- // Indicates whether fast timers are usable right now. For instance,
- // when using battery power, we might elect to prevent high speed timers
- // which would draw more power.
- static bool high_resolution_timer_enabled_;
- // Count of activations on the high resolution timer. Only use in tests
- // which are single threaded.
- static int high_resolution_timer_activated_;
-#endif
-
// Time in microseconds in UTC.
int64 us_;
};
//
// To work around all this, we're going to generally use timeGetTime(). We
// will only increase the system-wide timer if we're not running on battery
-// power. Using timeBeginPeriod(1) is a requirement in order to make our
-// message loop waits have the same resolution that our time measurements
-// do. Otherwise, WaitForSingleObject(..., 1) will no less than 15ms when
-// there is nothing else to waken the Wait.
+// power.
#include "base/time/time.h"
initial_time = CurrentWallclockMicroseconds();
}
+// The two values that ActivateHighResolutionTimer uses to set the systemwide
+// timer interrupt frequency on Windows. It controls how precise timers are
+// but also has a big impact on battery life.
+const int kMinTimerIntervalHighResMs = 1;
+const int kMinTimerIntervalLowResMs = 4;
+// Track if kMinTimerIntervalHighResMs or kMinTimerIntervalLowResMs is active.
+bool g_high_res_timer_enabled = false;
+// How many times the high resolution timer has been called.
+uint32_t g_high_res_timer_count = 0;
+// The lock to control access to the above two variables.
+base::LazyInstance<base::Lock>::Leaky g_high_res_lock =
+ LAZY_INSTANCE_INITIALIZER;
+
} // namespace
// Time -----------------------------------------------------------------------
// static
const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000);
-bool Time::high_resolution_timer_enabled_ = false;
-int Time::high_resolution_timer_activated_ = 0;
-
// static
Time Time::Now() {
if (initial_time == 0)
// static
void Time::EnableHighResolutionTimer(bool enable) {
- // Test for single-threaded access.
- static PlatformThreadId my_thread = PlatformThread::CurrentId();
- DCHECK(PlatformThread::CurrentId() == my_thread);
-
- if (high_resolution_timer_enabled_ == enable)
+ base::AutoLock lock(g_high_res_lock.Get());
+ if (g_high_res_timer_enabled == enable)
return;
-
- high_resolution_timer_enabled_ = enable;
+ g_high_res_timer_enabled = enable;
+ if (!g_high_res_timer_count)
+ return;
+ // Since g_high_res_timer_count != 0, an ActivateHighResolutionTimer(true)
+ // was called which called timeBeginPeriod with g_high_res_timer_enabled
+ // with a value which is the opposite of |enable|. With that information we
+ // call timeEndPeriod with the same value used in timeBeginPeriod and
+ // therefore undo the period effect.
+ if (enable) {
+ timeEndPeriod(kMinTimerIntervalLowResMs);
+ timeBeginPeriod(kMinTimerIntervalHighResMs);
+ } else {
+ timeEndPeriod(kMinTimerIntervalHighResMs);
+ timeBeginPeriod(kMinTimerIntervalLowResMs);
+ }
}
// static
bool Time::ActivateHighResolutionTimer(bool activating) {
- if (!high_resolution_timer_enabled_ && activating)
- return false;
-
- // Using anything other than 1ms makes timers granular
- // to that interval.
- const int kMinTimerIntervalMs = 1;
- MMRESULT result;
+ // We only do work on the transition from zero to one or one to zero so we
+ // can easily undo the effect (if necessary) when EnableHighResolutionTimer is
+ // called.
+ const uint32_t max = std::numeric_limits<uint32_t>::max();
+
+ base::AutoLock lock(g_high_res_lock.Get());
+ UINT period = g_high_res_timer_enabled ? kMinTimerIntervalHighResMs
+ : kMinTimerIntervalLowResMs;
if (activating) {
- result = timeBeginPeriod(kMinTimerIntervalMs);
- high_resolution_timer_activated_++;
+ DCHECK(g_high_res_timer_count != max);
+ ++g_high_res_timer_count;
+ if (g_high_res_timer_count == 1)
+ timeBeginPeriod(period);
} else {
- result = timeEndPeriod(kMinTimerIntervalMs);
- high_resolution_timer_activated_--;
+ DCHECK(g_high_res_timer_count != 0);
+ --g_high_res_timer_count;
+ if (g_high_res_timer_count == 0)
+ timeEndPeriod(period);
}
- return result == TIMERR_NOERROR;
+ return (period == kMinTimerIntervalHighResMs);
}
// static
bool Time::IsHighResolutionTimerInUse() {
- // Note: we should track the high_resolution_timer_activated_ value
- // under a lock if we want it to be accurate in a system with multiple
- // message loops. We don't do that - because we don't want to take the
- // expense of a lock for this. We *only* track this value so that unit
- // tests can see if the high resolution timer is on or off.
- return high_resolution_timer_enabled_ &&
- high_resolution_timer_activated_ > 0;
+ base::AutoLock lock(g_high_res_lock.Get());
+ return g_high_res_timer_enabled && g_high_res_timer_count > 0;
}
// static
namespace base {
#if defined(OS_WIN)
-// http://crbug.com/114048
-TEST(HiResTimerManagerTest, DISABLED_ToggleOnOff) {
- base::MessageLoop loop;
+TEST(HiResTimerManagerTest, ToggleOnOff) {
+ // The power monitor creates Window to receive power notifications from
+ // Windows, which makes this test flaky if you run while the machine
+ // goes in or out of AC power.
+ base::MessageLoop loop(base::MessageLoop::TYPE_UI);
scoped_ptr<base::PowerMonitorSource> power_monitor_source(
new base::PowerMonitorDeviceSource());
scoped_ptr<base::PowerMonitor> power_monitor(
new base::PowerMonitor(power_monitor_source.Pass()));
- HighResolutionTimerManager manager;
- // At this point, we don't know if the high resolution timers are on or off,
- // it depends on what system the tests are running on (for example, if this
- // test is running on a laptop/battery, then the PowerMonitor would have
- // already set the PowerState to battery power; but if we're running on a
- // desktop, then the PowerState will be non-battery power). Simulate a power
- // level change to get to a deterministic state.
- manager.OnPowerStateChange(/* on_battery */ false);
+ HighResolutionTimerManager manager;
+ // Simulate a on-AC power event to get to a known initial state.
+ manager.OnPowerStateChange(false);
// Loop a few times to test power toggling.
- for (int loop = 2; loop >= 0; --loop) {
+ for (int times = 0; times != 3; ++times) {
// The manager has the high resolution clock enabled now.
EXPECT_TRUE(manager.hi_res_clock_available());
// But the Time class has it off, because it hasn't been activated.
EXPECT_TRUE(base::Time::IsHighResolutionTimerInUse());
// Simulate a on-battery power event.
- manager.OnPowerStateChange(/* on_battery */ true);
+ manager.OnPowerStateChange(true);
EXPECT_FALSE(manager.hi_res_clock_available());
EXPECT_FALSE(base::Time::IsHighResolutionTimerInUse());
- // Simulate a off-battery power event.
- manager.OnPowerStateChange(/* on_battery */ false);
+ // Back to on-AC power.
+ manager.OnPowerStateChange(false);
EXPECT_TRUE(manager.hi_res_clock_available());
EXPECT_TRUE(base::Time::IsHighResolutionTimerInUse());
-LASTCHANGE=290690
+LASTCHANGE=291211
-LASTCHANGE=180871
+LASTCHANGE=182060
return std::min(std::max(y, 0), num_tiles_y_ - 1);
}
-gfx::Rect TilingData::ExpandRectIgnoringBordersToTileBoundsWithBorders(
+gfx::Rect TilingData::ExpandRectIgnoringBordersToTileBounds(
const gfx::Rect& rect) const {
if (rect.IsEmpty() || has_empty_bounds())
return gfx::Rect();
int index_right = TileXIndexFromSrcCoord(rect.right() - 1);
int index_bottom = TileYIndexFromSrcCoord(rect.bottom() - 1);
- gfx::Rect rect_top_left(TileBoundsWithBorder(index_x, index_y));
- gfx::Rect rect_bottom_right(TileBoundsWithBorder(index_right, index_bottom));
+ gfx::Rect rect_top_left(TileBounds(index_x, index_y));
+ gfx::Rect rect_bottom_right(TileBounds(index_right, index_bottom));
return gfx::UnionRects(rect_top_left, rect_bottom_right);
}
return;
}
- consider_left_ =
- tiling_data_->FirstBorderTileXIndexFromSrcCoord(consider.x());
- consider_top_ =
- tiling_data_->FirstBorderTileYIndexFromSrcCoord(consider.y());
- consider_right_ =
- tiling_data_->LastBorderTileXIndexFromSrcCoord(consider.right() - 1);
+ consider_left_ = tiling_data_->TileXIndexFromSrcCoord(consider.x());
+ consider_top_ = tiling_data_->TileYIndexFromSrcCoord(consider.y());
+ consider_right_ = tiling_data_->TileXIndexFromSrcCoord(consider.right() - 1);
consider_bottom_ =
- tiling_data_->LastBorderTileYIndexFromSrcCoord(consider.bottom() - 1);
+ tiling_data_->TileYIndexFromSrcCoord(consider.bottom() - 1);
if (!ignore.IsEmpty()) {
- ignore_left_ =
- tiling_data_->FirstBorderTileXIndexFromSrcCoord(ignore.x());
- ignore_top_ =
- tiling_data_->FirstBorderTileYIndexFromSrcCoord(ignore.y());
- ignore_right_ =
- tiling_data_->LastBorderTileXIndexFromSrcCoord(ignore.right() - 1);
- ignore_bottom_ =
- tiling_data_->LastBorderTileYIndexFromSrcCoord(ignore.bottom() - 1);
+ ignore_left_ = tiling_data_->TileXIndexFromSrcCoord(ignore.x());
+ ignore_top_ = tiling_data_->TileYIndexFromSrcCoord(ignore.y());
+ ignore_right_ = tiling_data_->TileXIndexFromSrcCoord(ignore.right() - 1);
+ ignore_bottom_ = tiling_data_->TileYIndexFromSrcCoord(ignore.bottom() - 1);
// Clamp ignore indices to consider indices.
ignore_left_ = std::max(ignore_left_, consider_left_);
return;
}
- consider_left_ =
- tiling_data_->FirstBorderTileXIndexFromSrcCoord(consider.x());
- consider_top_ = tiling_data_->FirstBorderTileYIndexFromSrcCoord(consider.y());
- consider_right_ =
- tiling_data_->LastBorderTileXIndexFromSrcCoord(consider.right() - 1);
+ consider_left_ = tiling_data_->TileXIndexFromSrcCoord(consider.x());
+ consider_top_ = tiling_data_->TileYIndexFromSrcCoord(consider.y());
+ consider_right_ = tiling_data_->TileXIndexFromSrcCoord(consider.right() - 1);
consider_bottom_ =
- tiling_data_->LastBorderTileYIndexFromSrcCoord(consider.bottom() - 1);
+ tiling_data_->TileYIndexFromSrcCoord(consider.bottom() - 1);
if (!ignore.IsEmpty()) {
- ignore_left_ = tiling_data_->FirstBorderTileXIndexFromSrcCoord(ignore.x());
- ignore_top_ = tiling_data_->FirstBorderTileYIndexFromSrcCoord(ignore.y());
- ignore_right_ =
- tiling_data_->LastBorderTileXIndexFromSrcCoord(ignore.right() - 1);
- ignore_bottom_ =
- tiling_data_->LastBorderTileYIndexFromSrcCoord(ignore.bottom() - 1);
+ ignore_left_ = tiling_data_->TileXIndexFromSrcCoord(ignore.x());
+ ignore_top_ = tiling_data_->TileYIndexFromSrcCoord(ignore.y());
+ ignore_right_ = tiling_data_->TileXIndexFromSrcCoord(ignore.right() - 1);
+ ignore_bottom_ = tiling_data_->TileYIndexFromSrcCoord(ignore.bottom() - 1);
// Clamp ignore indices to consider indices.
ignore_left_ = std::max(ignore_left_, consider_left_);
else if (center.x() > tiling_data->tiling_size().width())
around_left = tiling_data->num_tiles_x();
else
- around_left = tiling_data->FirstBorderTileXIndexFromSrcCoord(center.x());
+ around_left = tiling_data->TileXIndexFromSrcCoord(center.x());
// Determine around top, such that it is between -1 and num_tiles_y.
int around_top = 0;
else if (center.y() > tiling_data->tiling_size().height())
around_top = tiling_data->num_tiles_y();
else
- around_top = tiling_data->FirstBorderTileYIndexFromSrcCoord(center.y());
+ around_top = tiling_data->TileYIndexFromSrcCoord(center.y());
// Determine around right, such that it is between -1 and num_tiles_x.
int right_src_coord = center.right() - 1;
} else if (right_src_coord > tiling_data->tiling_size().width()) {
around_right = tiling_data->num_tiles_x();
} else {
- around_right =
- tiling_data->LastBorderTileXIndexFromSrcCoord(right_src_coord);
+ around_right = tiling_data->TileXIndexFromSrcCoord(right_src_coord);
}
// Determine around bottom, such that it is between -1 and num_tiles_y.
} else if (bottom_src_coord > tiling_data->tiling_size().height()) {
around_bottom = tiling_data->num_tiles_y();
} else {
- around_bottom =
- tiling_data->LastBorderTileYIndexFromSrcCoord(bottom_src_coord);
+ around_bottom = tiling_data->TileYIndexFromSrcCoord(bottom_src_coord);
}
vertical_step_count_ = around_bottom - around_top + 1;
int LastBorderTileXIndexFromSrcCoord(int src_position) const;
int LastBorderTileYIndexFromSrcCoord(int src_position) const;
- gfx::Rect ExpandRectIgnoringBordersToTileBoundsWithBorders(
- const gfx::Rect& rect) const;
+ gfx::Rect ExpandRectIgnoringBordersToTileBounds(const gfx::Rect& rect) const;
gfx::Rect ExpandRectToTileBounds(const gfx::Rect& rect) const;
gfx::Rect TileBounds(int i, int j) const;
int bottom_;
};
- // Iterate through all indices whose bounds + border intersect with
- // |consider| but which also do not intersect with |ignore|.
+ // Iterate through all indices whose bounds (not including borders) intersect
+ // with |consider| but which also do not intersect with |ignore|.
class CC_EXPORT DifferenceIterator : public BaseIterator {
public:
- DifferenceIterator(
- const TilingData* tiling_data,
- const gfx::Rect& consider_rect,
- const gfx::Rect& ignore_rect);
+ DifferenceIterator(const TilingData* tiling_data,
+ const gfx::Rect& consider_rect,
+ const gfx::Rect& ignore_rect);
DifferenceIterator& operator++();
private:
EXPECT_EQ(10, data.num_tiles_y());
}
-TEST(TilingDataTest, ExpandRectIgnoringBordersToTileBoundsWithBordersEmpty) {
+TEST(TilingDataTest, ExpandRectIgnoringBordersToTileBoundsEmpty) {
TilingData empty_total_size(gfx::Size(0, 0), gfx::Size(8, 8), true);
EXPECT_RECT_EQ(
gfx::Rect(),
- empty_total_size.ExpandRectIgnoringBordersToTileBoundsWithBorders(
- gfx::Rect()));
- EXPECT_RECT_EQ(
- gfx::Rect(),
- empty_total_size.ExpandRectIgnoringBordersToTileBoundsWithBorders(
- gfx::Rect(100, 100, 100, 100)));
- EXPECT_RECT_EQ(
- gfx::Rect(),
- empty_total_size.ExpandRectIgnoringBordersToTileBoundsWithBorders(
- gfx::Rect(100, 100)));
+ empty_total_size.ExpandRectIgnoringBordersToTileBounds(gfx::Rect()));
+ EXPECT_RECT_EQ(gfx::Rect(),
+ empty_total_size.ExpandRectIgnoringBordersToTileBounds(
+ gfx::Rect(100, 100, 100, 100)));
+ EXPECT_RECT_EQ(gfx::Rect(),
+ empty_total_size.ExpandRectIgnoringBordersToTileBounds(
+ gfx::Rect(100, 100)));
TilingData empty_max_texture_size(gfx::Size(8, 8), gfx::Size(0, 0), true);
- EXPECT_RECT_EQ(
- gfx::Rect(),
- empty_max_texture_size.ExpandRectIgnoringBordersToTileBoundsWithBorders(
- gfx::Rect()));
- EXPECT_RECT_EQ(
- gfx::Rect(),
- empty_max_texture_size.ExpandRectIgnoringBordersToTileBoundsWithBorders(
- gfx::Rect(100, 100, 100, 100)));
- EXPECT_RECT_EQ(
- gfx::Rect(),
- empty_max_texture_size.ExpandRectIgnoringBordersToTileBoundsWithBorders(
- gfx::Rect(100, 100)));
+ EXPECT_RECT_EQ(gfx::Rect(),
+ empty_max_texture_size.ExpandRectIgnoringBordersToTileBounds(
+ gfx::Rect()));
+ EXPECT_RECT_EQ(gfx::Rect(),
+ empty_max_texture_size.ExpandRectIgnoringBordersToTileBounds(
+ gfx::Rect(100, 100, 100, 100)));
+ EXPECT_RECT_EQ(gfx::Rect(),
+ empty_max_texture_size.ExpandRectIgnoringBordersToTileBounds(
+ gfx::Rect(100, 100)));
}
-TEST(TilingDataTest, ExpandRectIgnoringBordersToTileBoundsWithBorders) {
+TEST(TilingDataTest, ExpandRectIgnoringBordersToTileBounds) {
TilingData data(gfx::Size(4, 4), gfx::Size(16, 32), true);
// Small rect at origin rounds up to tile 0, 0.
gfx::Rect at_origin_src(1, 1);
- gfx::Rect at_origin_result(data.TileBoundsWithBorder(0, 0));
+ gfx::Rect at_origin_result(data.TileBounds(0, 0));
EXPECT_NE(at_origin_src, at_origin_result);
- EXPECT_RECT_EQ(
- at_origin_result,
- data.ExpandRectIgnoringBordersToTileBoundsWithBorders(at_origin_src));
+ EXPECT_RECT_EQ(at_origin_result,
+ data.ExpandRectIgnoringBordersToTileBounds(at_origin_src));
// Arbitrary internal rect.
gfx::Rect rect_src(6, 6, 1, 3);
// Tile 2, 2 => gfx::Rect(4, 4, 4, 4)
// Tile 2, 3 => gfx::Rect(4, 6, 4, 4)
- gfx::Rect rect_result(gfx::UnionRects(data.TileBoundsWithBorder(2, 2),
- data.TileBoundsWithBorder(2, 3)));
+ gfx::Rect rect_result(
+ gfx::UnionRects(data.TileBounds(2, 2), data.TileBounds(2, 3)));
EXPECT_NE(rect_src, rect_result);
- EXPECT_RECT_EQ(
- rect_result,
- data.ExpandRectIgnoringBordersToTileBoundsWithBorders(rect_src));
+ EXPECT_RECT_EQ(rect_result,
+ data.ExpandRectIgnoringBordersToTileBounds(rect_src));
// On tile bounds does not round up to next tile (ignores the border).
gfx::Rect border_rect_src(
gfx::UnionRects(data.TileBounds(1, 2), data.TileBounds(3, 4)));
- gfx::Rect border_rect_result(gfx::UnionRects(
- data.TileBoundsWithBorder(1, 2), data.TileBoundsWithBorder(3, 4)));
- EXPECT_RECT_EQ(
- border_rect_result,
- data.ExpandRectIgnoringBordersToTileBoundsWithBorders(border_rect_src));
+ gfx::Rect border_rect_result(
+ gfx::UnionRects(data.TileBounds(1, 2), data.TileBounds(3, 4)));
+ EXPECT_RECT_EQ(border_rect_result,
+ data.ExpandRectIgnoringBordersToTileBounds(border_rect_src));
// Equal to tiling rect.
EXPECT_RECT_EQ(gfx::Rect(data.tiling_size()),
- data.ExpandRectIgnoringBordersToTileBoundsWithBorders(
+ data.ExpandRectIgnoringBordersToTileBounds(
gfx::Rect(data.tiling_size())));
// Containing, but larger than tiling rect.
- EXPECT_RECT_EQ(gfx::Rect(data.tiling_size()),
- data.ExpandRectIgnoringBordersToTileBoundsWithBorders(
- gfx::Rect(100, 100)));
+ EXPECT_RECT_EQ(
+ gfx::Rect(data.tiling_size()),
+ data.ExpandRectIgnoringBordersToTileBounds(gfx::Rect(100, 100)));
// Non-intersecting with tiling rect.
gfx::Rect non_intersect(200, 200, 100, 100);
EXPECT_FALSE(non_intersect.Intersects(gfx::Rect(data.tiling_size())));
- EXPECT_RECT_EQ(
- gfx::Rect(),
- data.ExpandRectIgnoringBordersToTileBoundsWithBorders(non_intersect));
+ EXPECT_RECT_EQ(gfx::Rect(),
+ data.ExpandRectIgnoringBordersToTileBounds(non_intersect));
TilingData data2(gfx::Size(8, 8), gfx::Size(32, 64), true);
// Inside other tile border texels doesn't include other tiles.
gfx::Rect inner_rect_src(data2.TileBounds(1, 1));
inner_rect_src.Inset(data2.border_texels(), data.border_texels());
- gfx::Rect inner_rect_result(data2.TileBoundsWithBorder(1, 1));
+ gfx::Rect inner_rect_result(data2.TileBounds(1, 1));
gfx::Rect expanded =
- data2.ExpandRectIgnoringBordersToTileBoundsWithBorders(inner_rect_src);
+ data2.ExpandRectIgnoringBordersToTileBounds(inner_rect_src);
EXPECT_EQ(inner_rect_result.ToString(), expanded.ToString());
}
}
// Make sure this also works with a difference iterator and an empty ignore.
- // The difference iterator always includes borders, so ignore it otherwise.
- if (include_borders) {
+ // The difference iterator never includes borders, so ignore it otherwise.
+ if (!include_borders) {
std::vector<std::pair<int, int> > expected = original_expected;
- for (TilingData::DifferenceIterator iter(&data, rect, gfx::Rect());
- iter; ++iter) {
+ for (TilingData::DifferenceIterator iter(&data, rect, gfx::Rect()); iter;
+ ++iter) {
bool found = false;
for (size_t i = 0; i < expected.size(); ++i) {
if (expected[i] == iter.index()) {
TestIterateAll(data, gfx::Rect(100, 100), 0, 0, -1, -1);
}
-void TestDiff(
- const TilingData& data,
- gfx::Rect consider,
- gfx::Rect ignore,
- size_t num_tiles) {
-
+void TestDiff(const TilingData& data,
+ gfx::Rect consider,
+ gfx::Rect ignore,
+ size_t num_tiles) {
std::vector<std::pair<int, int> > expected;
for (int y = 0; y < data.num_tiles_y(); ++y) {
for (int x = 0; x < data.num_tiles_x(); ++x) {
- gfx::Rect bounds = data.TileBoundsWithBorder(x, y);
+ gfx::Rect bounds = data.TileBounds(x, y);
if (bounds.Intersects(consider) && !bounds.Intersects(ignore))
expected.push_back(std::make_pair(x, y));
}
// Sanity check the test.
EXPECT_EQ(num_tiles, expected.size());
- for (TilingData::DifferenceIterator iter(&data, consider, ignore);
- iter; ++iter) {
+ for (TilingData::DifferenceIterator iter(&data, consider, ignore); iter;
+ ++iter) {
bool found = false;
for (size_t i = 0; i < expected.size(); ++i) {
if (expected[i] == iter.index()) {
TEST(TilingDataTest, DifferenceIteratorManyBorderTexels) {
// X border index by src coord: [0-50), [10-60), [20-65)
// Y border index by src coord: [0-60), [20-80), [40-100), [60-110)
+ // X tile bounds by src coord: [0-30), [30-40), [40-65)
+ // Y tile bounds by src coord: [0-40), [40-60), [60-80), [80-110)
TilingData data(gfx::Size(50, 60), gfx::Size(65, 110), 20);
- // Ignore one column, three rows
- TestDiff(data, gfx::Rect(0, 30, 55, 80), gfx::Rect(5, 30, 5, 15), 9);
+ // Knock out two rows, but not the left column.
+ TestDiff(data, gfx::Rect(10, 30, 55, 80), gfx::Rect(30, 59, 20, 2), 8);
- // Knock out three columns, leaving only one.
- TestDiff(data, gfx::Rect(10, 30, 55, 80), gfx::Rect(30, 59, 20, 1), 3);
+ // Knock out one row.
+ TestDiff(data, gfx::Rect(10, 30, 55, 80), gfx::Rect(29, 59, 20, 1), 9);
// Overlap all tiles with ignore rect.
- TestDiff(data, gfx::Rect(65, 110), gfx::Rect(30, 59, 1, 2), 0);
+ TestDiff(data, gfx::Rect(65, 110), gfx::Rect(29, 39, 12, 42), 0);
+
+ gfx::Rect tile = data.TileBounds(1, 1);
+
+ // Ignore one tile.
+ TestDiff(data, gfx::Rect(20, 30, 45, 80), tile, 11);
+
+ // Include one tile.
+ TestDiff(data, tile, gfx::Rect(), 1);
}
TEST(TilingDataTest, DifferenceIteratorOneTile) {
// TODO(wjmaclean) Convert so that bounds returns SizeF.
gfx::Size LayerImpl::bounds() const {
- return ToFlooredSize(temporary_impl_bounds_);
+ return ToCeiledSize(temporary_impl_bounds_);
}
void LayerImpl::SetBounds(const gfx::Size& bounds) {
: client_(client),
pile_(make_scoped_refptr(new PicturePile())),
instrumentation_object_tracker_(id()),
- is_mask_(false),
update_source_frame_number_(-1),
can_use_lcd_text_last_frame_(can_use_lcd_text()) {
}
DCHECK_EQ(layer_impl->bounds().ToString(), pile_->tiling_size().ToString());
}
- layer_impl->SetIsMask(is_mask_);
-
// Unlike other properties, invalidation must always be set on layer_impl.
// See PictureLayerImpl::PushPropertiesTo for more details.
layer_impl->invalidation_.Clear();
}
void PictureLayer::SetIsMask(bool is_mask) {
- is_mask_ = is_mask;
+ pile_->set_is_mask(is_mask);
}
Picture::RecordingMode PictureLayer::RecordingMode() const {
: LayerImpl(tree_impl, id),
twin_layer_(NULL),
pile_(PicturePileImpl::Create()),
- is_mask_(false),
ideal_page_scale_(0.f),
ideal_device_scale_(0.f),
ideal_source_scale_(0.f),
LayerImpl::PushPropertiesTo(base_layer);
// When the pending tree pushes to the active tree, the pending twin
- // disappears.
+ // becomes recycled.
layer_impl->twin_layer_ = NULL;
twin_layer_ = NULL;
- layer_impl->SetIsMask(is_mask_);
layer_impl->pile_ = pile_;
// Tilings would be expensive to push, so we swap.
// unused can be considered for removal.
std::vector<PictureLayerTiling*> seen_tilings;
+ // Ignore missing tiles outside of viewport for tile priority. This is
+ // normally the same as draw viewport but can be independently overridden by
+ // embedders like Android WebView with SetExternalDrawConstraints.
+ gfx::Rect scaled_viewport_for_tile_priority = gfx::ScaleToEnclosingRect(
+ GetViewportForTilePriorityInContentSpace(), max_contents_scale);
+
size_t missing_tile_count = 0u;
size_t on_demand_missing_tile_count = 0u;
for (PictureLayerTilingSet::CoverageIterator iter(tilings_.get(),
append_quads_data->visible_content_area +=
visible_geometry_rect.width() * visible_geometry_rect.height();
+ bool has_draw_quad = false;
if (*iter && iter->IsReadyToDraw()) {
const ManagedTileState::TileVersion& tile_version =
iter->GetTileVersionForDrawing();
gfx::Rect opaque_rect = iter->opaque_rect();
opaque_rect.Intersect(geometry_rect);
- if (iter->contents_scale() != ideal_contents_scale_)
+ if (iter->contents_scale() != ideal_contents_scale_ &&
+ geometry_rect.Intersects(scaled_viewport_for_tile_priority)) {
append_quads_data->num_incomplete_tiles++;
+ }
TileDrawQuad* quad =
render_pass->CreateAndAppendDrawQuad<TileDrawQuad>();
texture_rect,
iter.texture_size(),
tile_version.contents_swizzled());
+ has_draw_quad = true;
break;
}
case ManagedTileState::TileVersion::PICTURE_PILE_MODE: {
iter->content_rect(),
iter->contents_scale(),
pile_);
+ has_draw_quad = true;
break;
}
case ManagedTileState::TileVersion::SOLID_COLOR_MODE: {
visible_geometry_rect,
tile_version.get_solid_color(),
false);
+ has_draw_quad = true;
break;
}
}
- } else {
+ }
+
+ if (!has_draw_quad) {
if (draw_checkerboard_for_missing_tiles()) {
CheckerboardDrawQuad* quad =
render_pass->CreateAndAppendDrawQuad<CheckerboardDrawQuad>();
false);
}
- append_quads_data->num_missing_tiles++;
+ if (geometry_rect.Intersects(scaled_viewport_for_tile_priority)) {
+ append_quads_data->num_missing_tiles++;
+ ++missing_tile_count;
+ }
append_quads_data->approximated_visible_content_area +=
visible_geometry_rect.width() * visible_geometry_rect.height();
- ++missing_tile_count;
continue;
}
if (!tiling_needs_update)
return;
+ gfx::Rect visible_rect_in_content_space(
+ GetViewportForTilePriorityInContentSpace());
+ visible_rect_in_content_space.Intersect(visible_content_rect());
+ gfx::Rect visible_layer_rect = gfx::ScaleToEnclosingRect(
+ visible_rect_in_content_space, 1.f / contents_scale_x());
+ WhichTree tree =
+ layer_tree_impl()->IsActiveTree() ? ACTIVE_TREE : PENDING_TREE;
+ for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
+ tilings_->tiling_at(i)->UpdateTilePriorities(tree,
+ visible_layer_rect,
+ ideal_contents_scale_,
+ current_frame_time_in_seconds,
+ occlusion_tracker,
+ render_target(),
+ draw_transform());
+ }
+
+ // Tile priorities were modified.
+ layer_tree_impl()->DidModifyTilePriorities();
+}
+
+gfx::Rect PictureLayerImpl::GetViewportForTilePriorityInContentSpace() const {
// If visible_rect_for_tile_priority_ is empty or
// viewport_rect_for_tile_priority_ is set to be different from the device
// viewport, try to inverse project the viewport into layer space and use
}
}
- gfx::Rect visible_layer_rect = gfx::ScaleToEnclosingRect(
- visible_rect_in_content_space, 1.f / contents_scale_x());
- WhichTree tree =
- layer_tree_impl()->IsActiveTree() ? ACTIVE_TREE : PENDING_TREE;
- for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
- tilings_->tiling_at(i)->UpdateTilePriorities(tree,
- visible_layer_rect,
- ideal_contents_scale_,
- current_frame_time_in_seconds,
- occlusion_tracker,
- render_target(),
- draw_transform());
- }
+ return visible_rect_in_content_space;
+}
- // Tile priorities were modified.
- layer_tree_impl()->DidModifyTilePriorities();
+PictureLayerImpl* PictureLayerImpl::GetRecycledTwinLayer() {
+ // TODO(vmpstr): Maintain recycled twin as a member. crbug.com/407418
+ return static_cast<PictureLayerImpl*>(
+ layer_tree_impl()->FindRecycleTreeLayerById(id()));
}
void PictureLayerImpl::NotifyTileStateChanged(const Tile* tile) {
// TODO(vmpstr): Revisit this. For now, enabling analysis means that we get as
// much savings on memory as we can. However, for some cases like ganesh or
// small layers, the amount of time we spend analyzing might not justify
- // memory savings that we can get.
+ // memory savings that we can get. Note that we don't handle solid color
+ // masks, so we shouldn't bother analyzing those.
// Bugs: crbug.com/397198, crbug.com/396908
- int flags = Tile::USE_PICTURE_ANALYSIS;
+ int flags = 0;
+ if (!pile_->is_mask())
+ flags = Tile::USE_PICTURE_ANALYSIS;
return layer_tree_impl()->tile_manager()->CreateTile(
pile_.get(),
return NULL;
}
+PictureLayerTiling* PictureLayerImpl::GetRecycledTwinTiling(
+ const PictureLayerTiling* tiling) {
+ PictureLayerImpl* recycled_twin = GetRecycledTwinLayer();
+ if (!recycled_twin || !recycled_twin->tilings_)
+ return NULL;
+ return recycled_twin->tilings_->TilingAtScale(tiling->contents_scale());
+}
+
size_t PictureLayerImpl::GetMaxTilesForInterestArea() const {
return layer_tree_impl()->settings().max_tiles_for_interest_area;
}
gfx::Size PictureLayerImpl::CalculateTileSize(
const gfx::Size& content_bounds) const {
- if (is_mask_) {
- int max_size = layer_tree_impl()->MaxTextureSize();
- return gfx::Size(
- std::min(max_size, content_bounds.width()),
- std::min(max_size, content_bounds.height()));
- }
-
int max_texture_size =
layer_tree_impl()->resource_provider()->max_texture_size();
+ if (pile_->is_mask()) {
+ // Masks are not tiled, so if we can't cover the whole mask with one tile,
+ // don't make any tiles at all. Returning an empty size signals this.
+ if (content_bounds.width() > max_texture_size ||
+ content_bounds.height() > max_texture_size)
+ return gfx::Size();
+ return content_bounds;
+ }
+
gfx::Size default_tile_size = layer_tree_impl()->settings().default_tile_size;
if (layer_tree_impl()->use_gpu_rasterization()) {
// TODO(ernstm) crbug.com/365877: We need a unified way to override the
}
}
-void PictureLayerImpl::SetIsMask(bool is_mask) {
- if (is_mask_ == is_mask)
- return;
- is_mask_ = is_mask;
- if (tilings_)
- tilings_->RemoveAllTiles();
-}
-
ResourceProvider::ResourceId PictureLayerImpl::ContentsResourceId() const {
gfx::Rect content_rect(content_bounds());
float scale = MaximumTilingContentsScale();
return 0;
// Masks only supported if they fit on exactly one tile.
- if (iter.geometry_rect() != content_rect)
- return 0;
+ DCHECK(iter.geometry_rect() == content_rect)
+ << "iter rect " << iter.geometry_rect().ToString() << " content rect "
+ << content_rect.ToString();
const ManagedTileState::TileVersion& tile_version =
iter->GetTileVersionForDrawing();
gfx::Rect rect(visible_content_rect());
+ // Only mark tiles inside the viewport for tile priority as required for
+ // activation. This viewport is normally the same as the draw viewport but
+ // can be independently overridden by embedders like Android WebView with
+ // SetExternalDrawConstraints.
+ rect.Intersect(GetViewportForTilePriorityInContentSpace());
+
float min_acceptable_scale =
std::min(raster_contents_scale_, ideal_contents_scale_);
}
}
- // If this layer would only create one tile at this content scale,
+ // If this layer would create zero or one tiles at this content scale,
// don't create a low res tiling.
gfx::Size content_bounds =
gfx::ToCeiledSize(gfx::ScaleSize(bounds(), raster_contents_scale_));
gfx::Size tile_size = CalculateTileSize(content_bounds);
- if (tile_size.width() >= content_bounds.width() &&
- tile_size.height() >= content_bounds.height()) {
+ bool tile_covers_bounds = tile_size.width() >= content_bounds.width() &&
+ tile_size.height() >= content_bounds.height();
+ if (tile_size.IsEmpty() || tile_covers_bounds) {
low_res_raster_contents_scale_ = raster_contents_scale_;
return;
}
if (to_remove.empty())
return;
- PictureLayerImpl* recycled_twin = static_cast<PictureLayerImpl*>(
- layer_tree_impl()->FindRecycleTreeLayerById(id()));
+ PictureLayerImpl* recycled_twin = GetRecycledTwinLayer();
// Remove tilings on this tree and the twin tree.
for (size_t i = 0; i < to_remove.size(); ++i) {
const PictureLayerTiling* twin_tiling = GetTwinTiling(to_remove[i]);
virtual const Region* GetInvalidation() OVERRIDE;
virtual const PictureLayerTiling* GetTwinTiling(
const PictureLayerTiling* tiling) const OVERRIDE;
+ virtual PictureLayerTiling* GetRecycledTwinTiling(
+ const PictureLayerTiling* tiling) OVERRIDE;
virtual size_t GetMaxTilesForInterestArea() const OVERRIDE;
virtual float GetSkewportTargetTimeInSeconds() const OVERRIDE;
virtual int GetSkewportExtrapolationLimitInContentPixels() const OVERRIDE;
// PushPropertiesTo active tree => pending tree.
void SyncTiling(const PictureLayerTiling* tiling);
- // Mask-related functions
- void SetIsMask(bool is_mask);
+ // Mask-related functions.
virtual ResourceProvider::ResourceId ContentsResourceId() const OVERRIDE;
virtual size_t GPUMemoryUsageInBytes() const OVERRIDE;
float contents_scale,
const gfx::Rect& rect,
const Region& missing_region) const;
+ gfx::Rect GetViewportForTilePriorityInContentSpace() const;
+ PictureLayerImpl* GetRecycledTwinLayer();
void DoPostCommitInitializationIfNeeded() {
if (needs_post_commit_initialization_)
scoped_refptr<PicturePileImpl> pile_;
Region invalidation_;
- bool is_mask_;
-
float ideal_page_scale_;
float ideal_device_scale_;
float ideal_source_scale_;
host_impl_(ImplSidePaintingSettings(),
&proxy_,
&shared_bitmap_manager_),
- id_(7) {}
+ id_(7),
+ pending_layer_(NULL),
+ old_pending_layer_(NULL),
+ active_layer_(NULL) {}
explicit PictureLayerImplTest(const LayerTreeSettings& settings)
: proxy_(base::MessageLoopProxy::current()),
void ActivateTree() {
host_impl_.ActivateSyncTree();
CHECK(!host_impl_.pending_tree());
+ CHECK(host_impl_.recycle_tree());
+ old_pending_layer_ = pending_layer_;
pending_layer_ = NULL;
active_layer_ = static_cast<FakePictureLayerImpl*>(
host_impl_.active_tree()->LayerById(id_));
SetupPendingTree(active_pile);
ActivateTree();
SetupPendingTree(pending_pile);
- host_impl_.pending_tree()->SetPageScaleFactorAndLimits(1.f, 0.25f, 100.f);
- host_impl_.active_tree()->SetPageScaleFactorAndLimits(1.f, 0.25f, 100.f);
}
void CreateHighLowResAndSetAllTilesVisible() {
void SetupPendingTree(scoped_refptr<PicturePileImpl> pile) {
host_impl_.CreatePendingTree();
+ host_impl_.pending_tree()->SetPageScaleFactorAndLimits(1.f, 0.25f, 100.f);
LayerTreeImpl* pending_tree = host_impl_.pending_tree();
// Clear recycled tree.
pending_tree->DetachLayerTree();
FakeLayerTreeHostImpl host_impl_;
int id_;
FakePictureLayerImpl* pending_layer_;
+ FakePictureLayerImpl* old_pending_layer_;
FakePictureLayerImpl* active_layer_;
private:
EXPECT_EQ(x, active_layer_->expression); \
} while (false)
+#define EXPECT_BOTH_NE(expression, x) \
+ do { \
+ EXPECT_NE(x, pending_layer_->expression); \
+ EXPECT_NE(x, active_layer_->expression); \
+ } while (false)
+
TEST_F(PictureLayerImplTest, DontAddLowResDuringAnimation) {
// Make sure this layer covers multiple tiles, since otherwise low
// res won't get created because it is too small.
ResetTilingsAndRasterScales();
// Mask layers dont create low res since they always fit on one tile.
- pending_layer_->SetIsMask(true);
- active_layer_->SetIsMask(true);
+ pending_layer_->pile()->set_is_mask(true);
+ active_layer_->pile()->set_is_mask(true);
SetContentsScaleOnBothLayers(contents_scale,
device_scale,
page_scale,
EXPECT_BOTH_EQ(num_tilings(), 1u);
}
+TEST_F(PictureLayerImplTest, HugeMasksDontGetTiles) {
+ gfx::Size tile_size(100, 100);
+
+ scoped_refptr<FakePicturePileImpl> valid_pile =
+ FakePicturePileImpl::CreateFilledPile(tile_size, gfx::Size(1000, 1000));
+ valid_pile->set_is_mask(true);
+ SetupPendingTree(valid_pile);
+
+ SetupDrawPropertiesAndUpdateTiles(pending_layer_, 1.f, 1.f, 1.f, 1.f, false);
+ EXPECT_EQ(1.f, pending_layer_->HighResTiling()->contents_scale());
+ EXPECT_EQ(1u, pending_layer_->num_tilings());
+
+ pending_layer_->HighResTiling()->CreateAllTilesForTesting();
+ host_impl_.tile_manager()->InitializeTilesWithResourcesForTesting(
+ pending_layer_->HighResTiling()->AllTilesForTesting());
+
+ ActivateTree();
+
+ // Mask layers have a tiling with a single tile in it.
+ EXPECT_EQ(1u, active_layer_->HighResTiling()->AllTilesForTesting().size());
+ // The mask resource exists.
+ EXPECT_NE(0u, active_layer_->ContentsResourceId());
+
+ // Resize larger than the max texture size.
+ int max_texture_size = host_impl_.GetRendererCapabilities().max_texture_size;
+ scoped_refptr<FakePicturePileImpl> huge_pile =
+ FakePicturePileImpl::CreateFilledPile(
+ tile_size, gfx::Size(max_texture_size + 1, 10));
+ huge_pile->set_is_mask(true);
+ SetupPendingTree(huge_pile);
+
+ SetupDrawPropertiesAndUpdateTiles(pending_layer_, 1.f, 1.f, 1.f, 1.f, false);
+ EXPECT_EQ(1.f, pending_layer_->HighResTiling()->contents_scale());
+ EXPECT_EQ(1u, pending_layer_->num_tilings());
+
+ pending_layer_->HighResTiling()->CreateAllTilesForTesting();
+ host_impl_.tile_manager()->InitializeTilesWithResourcesForTesting(
+ pending_layer_->HighResTiling()->AllTilesForTesting());
+
+ ActivateTree();
+
+ // Mask layers have a tiling, but there should be no tiles in it.
+ EXPECT_EQ(0u, active_layer_->HighResTiling()->AllTilesForTesting().size());
+ // The mask resource is empty.
+ EXPECT_EQ(0u, active_layer_->ContentsResourceId());
+}
+
TEST_F(PictureLayerImplTest, ReleaseResources) {
gfx::Size tile_size(400, 400);
gfx::Size layer_bounds(1300, 1900);
EXPECT_GT(num_offscreen, 0);
}
+TEST_F(PictureLayerImplTest, TileOutsideOfViewportForTilePriorityNotRequired) {
+ base::TimeTicks time_ticks;
+ time_ticks += base::TimeDelta::FromMilliseconds(1);
+ host_impl_.SetCurrentFrameTimeTicks(time_ticks);
+
+ gfx::Size tile_size(100, 100);
+ gfx::Size layer_bounds(400, 400);
+ gfx::Rect external_viewport_for_tile_priority(400, 200);
+ gfx::Rect visible_content_rect(200, 400);
+
+ scoped_refptr<FakePicturePileImpl> active_pile =
+ FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+ scoped_refptr<FakePicturePileImpl> pending_pile =
+ FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+ SetupTrees(pending_pile, active_pile);
+
+ active_layer_->set_fixed_tile_size(tile_size);
+ pending_layer_->set_fixed_tile_size(tile_size);
+ ASSERT_TRUE(pending_layer_->CanHaveTilings());
+ PictureLayerTiling* tiling = pending_layer_->AddTiling(1.f);
+
+ // Set external viewport for tile priority.
+ gfx::Rect viewport = gfx::Rect(layer_bounds);
+ gfx::Transform transform;
+ gfx::Transform transform_for_tile_priority;
+ bool resourceless_software_draw = false;
+ host_impl_.SetExternalDrawConstraints(transform,
+ viewport,
+ viewport,
+ external_viewport_for_tile_priority,
+ transform_for_tile_priority,
+ resourceless_software_draw);
+ host_impl_.pending_tree()->UpdateDrawProperties();
+
+ // Set visible content rect that is different from
+ // external_viewport_for_tile_priority.
+ pending_layer_->draw_properties().visible_content_rect = visible_content_rect;
+ time_ticks += base::TimeDelta::FromMilliseconds(200);
+ host_impl_.SetCurrentFrameTimeTicks(time_ticks);
+ pending_layer_->UpdateTiles(NULL);
+
+ pending_layer_->MarkVisibleResourcesAsRequired();
+
+ // Intersect the two rects. Any tile outside should not be required for
+ // activation.
+ gfx::Rect viewport_for_tile_priority =
+ pending_layer_->GetViewportForTilePriorityInContentSpace();
+ viewport_for_tile_priority.Intersect(pending_layer_->visible_content_rect());
+
+ int num_inside = 0;
+ int num_outside = 0;
+ for (PictureLayerTiling::CoverageIterator iter(
+ tiling, pending_layer_->contents_scale_x(), gfx::Rect(layer_bounds));
+ iter;
+ ++iter) {
+ if (!*iter)
+ continue;
+ Tile* tile = *iter;
+ if (viewport_for_tile_priority.Intersects(iter.geometry_rect())) {
+ num_inside++;
+ // Mark everything in viewport for tile priority as ready to draw.
+ ManagedTileState::TileVersion& tile_version =
+ tile->GetTileVersionForTesting(
+ tile->DetermineRasterModeForTree(PENDING_TREE));
+ tile_version.SetSolidColorForTesting(SK_ColorRED);
+ } else {
+ num_outside++;
+ EXPECT_FALSE(tile->required_for_activation());
+ }
+ }
+
+ EXPECT_GT(num_inside, 0);
+ EXPECT_GT(num_outside, 0);
+
+ // Activate and draw active layer.
+ host_impl_.ActivateSyncTree();
+ host_impl_.active_tree()->UpdateDrawProperties();
+ active_layer_->draw_properties().visible_content_rect = visible_content_rect;
+
+ MockOcclusionTracker<LayerImpl> occlusion_tracker;
+ scoped_ptr<RenderPass> render_pass = RenderPass::Create();
+ AppendQuadsData data;
+ active_layer_->WillDraw(DRAW_MODE_SOFTWARE, NULL);
+ active_layer_->AppendQuads(render_pass.get(), occlusion_tracker, &data);
+ active_layer_->DidDraw(NULL);
+
+ // All tiles in activation rect is ready to draw.
+ EXPECT_EQ(0u, data.num_missing_tiles);
+ EXPECT_EQ(0u, data.num_incomplete_tiles);
+}
+
TEST_F(PictureLayerImplTest, HighResRequiredWhenUnsharedActiveAllReady) {
gfx::Size layer_bounds(400, 400);
gfx::Size tile_size(100, 100);
scoped_refptr<FakePicturePileImpl> pending_pile =
FakePicturePileImpl::CreateFilledPile(tile_size, bounds);
+ pending_pile->set_is_mask(true);
scoped_ptr<FakePictureLayerImpl> mask = FakePictureLayerImpl::CreateWithPile(
host_impl_.pending_tree(), 3, pending_pile);
- mask->SetIsMask(true);
mask->SetBounds(bounds);
mask->SetContentBounds(bounds);
mask->SetDrawsContent(true);
EXPECT_NE(0u, pending_mask_content->num_tilings());
}
+class PictureLayerImplTestWithDelegatingRenderer : public PictureLayerImplTest {
+ public:
+ PictureLayerImplTestWithDelegatingRenderer() : PictureLayerImplTest() {}
+
+ virtual void InitializeRenderer() OVERRIDE {
+ host_impl_.InitializeRenderer(
+ FakeOutputSurface::CreateDelegating3d().PassAs<OutputSurface>());
+ }
+};
+
+TEST_F(PictureLayerImplTestWithDelegatingRenderer,
+ DelegatingRendererWithTileOOM) {
+ // This test is added for crbug.com/402321, where quad should be produced when
+ // raster on demand is not allowed and tile is OOM.
+ gfx::Size tile_size = host_impl_.settings().default_tile_size;
+ gfx::Size layer_bounds(1000, 1000);
+
+ // Create tiles.
+ scoped_refptr<FakePicturePileImpl> pending_pile =
+ FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+ SetupPendingTree(pending_pile);
+ pending_layer_->SetBounds(layer_bounds);
+ host_impl_.SetViewportSize(layer_bounds);
+ ActivateTree();
+ host_impl_.active_tree()->UpdateDrawProperties();
+ std::vector<Tile*> tiles =
+ active_layer_->HighResTiling()->AllTilesForTesting();
+ host_impl_.tile_manager()->InitializeTilesWithResourcesForTesting(tiles);
+
+ // Force tiles after max_tiles to be OOM. TileManager uses
+ // GlobalStateThatImpactsTilesPriority from LayerTreeHostImpl, and we cannot
+ // directly set state to host_impl_, so we set policy that would change the
+ // state. We also need to update tree priority separately.
+ GlobalStateThatImpactsTilePriority state;
+ size_t max_tiles = 1;
+ size_t memory_limit = max_tiles * 4 * tile_size.width() * tile_size.height();
+ size_t resource_limit = max_tiles;
+ ManagedMemoryPolicy policy(memory_limit,
+ gpu::MemoryAllocation::CUTOFF_ALLOW_EVERYTHING,
+ resource_limit);
+ host_impl_.SetMemoryPolicy(policy);
+ host_impl_.SetTreePriority(SAME_PRIORITY_FOR_BOTH_TREES);
+ host_impl_.ManageTiles();
+
+ MockOcclusionTracker<LayerImpl> occlusion_tracker;
+ scoped_ptr<RenderPass> render_pass = RenderPass::Create();
+ AppendQuadsData data;
+ active_layer_->WillDraw(DRAW_MODE_HARDWARE, NULL);
+ active_layer_->AppendQuads(render_pass.get(), occlusion_tracker, &data);
+ active_layer_->DidDraw(NULL);
+
+ // Even when OOM, quads should be produced, and should be different material
+ // from quads with resource.
+ EXPECT_LT(max_tiles, render_pass->quad_list.size());
+ EXPECT_EQ(DrawQuad::Material::TILED_CONTENT,
+ render_pass->quad_list.front()->material);
+ EXPECT_EQ(DrawQuad::Material::SOLID_COLOR,
+ render_pass->quad_list.back()->material);
+}
+
class OcclusionTrackingSettings : public ImplSidePaintingSettings {
public:
OcclusionTrackingSettings() { use_occlusion_for_tile_prioritization = true; }
VerifyEvictionConsidersOcclusion(active_layer_,
total_expected_occluded_tile_count);
}
+
+TEST_F(PictureLayerImplTest, RecycledTwinLayer) {
+ gfx::Size tile_size(102, 102);
+ gfx::Size layer_bounds(1000, 1000);
+
+ scoped_refptr<FakePicturePileImpl> pile =
+ FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+ SetupPendingTree(pile);
+ EXPECT_FALSE(pending_layer_->GetRecycledTwinLayer());
+
+ ActivateTree();
+ EXPECT_TRUE(active_layer_->GetRecycledTwinLayer());
+ EXPECT_EQ(old_pending_layer_, active_layer_->GetRecycledTwinLayer());
+
+ SetupPendingTree(pile);
+ EXPECT_FALSE(pending_layer_->GetRecycledTwinLayer());
+ EXPECT_FALSE(active_layer_->GetRecycledTwinLayer());
+
+ ActivateTree();
+ EXPECT_TRUE(active_layer_->GetRecycledTwinLayer());
+ EXPECT_EQ(old_pending_layer_, active_layer_->GetRecycledTwinLayer());
+
+ host_impl_.ResetRecycleTreeForTesting();
+ EXPECT_FALSE(active_layer_->GetRecycledTwinLayer());
+}
+
} // namespace
} // namespace cc
#include "base/debug/trace_event.h"
#include "base/debug/trace_event_argument.h"
+#include "base/logging.h"
#include "cc/base/math_util.h"
#include "cc/resources/tile.h"
#include "cc/resources/tile_priority.h"
gfx::Size content_bounds =
gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds, contents_scale));
gfx::Size tile_size = client_->CalculateTileSize(content_bounds);
+ if (tile_size.IsEmpty()) {
+ layer_bounds_ = gfx::Size();
+ content_bounds = gfx::Size();
+ }
DCHECK(!gfx::ToFlooredSize(
gfx::ScaleSize(layer_bounds, contents_scale)).IsEmpty()) <<
void PictureLayerTiling::CreateMissingTilesInLiveTilesRect() {
const PictureLayerTiling* twin_tiling = client_->GetTwinTiling(this);
- bool include_borders = true;
+ bool include_borders = false;
for (TilingData::Iterator iter(
&tiling_data_, live_tiles_rect_, include_borders);
iter;
continue;
CreateTile(key.first, key.second, twin_tiling);
}
+
+ VerifyLiveTilesRect();
}
void PictureLayerTiling::UpdateTilesToCurrentPile(
const gfx::Size& new_layer_bounds) {
DCHECK(!new_layer_bounds.IsEmpty());
- gfx::Size old_layer_bounds = layer_bounds_;
- layer_bounds_ = new_layer_bounds;
-
- gfx::Size content_bounds =
- gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds_, contents_scale_));
gfx::Size tile_size = tiling_data_.max_texture_size();
- if (layer_bounds_ != old_layer_bounds) {
- // Drop tiles outside the new layer bounds if the layer shrank.
- SetLiveTilesRect(
- gfx::IntersectRects(live_tiles_rect_, gfx::Rect(content_bounds)));
- tiling_data_.SetTilingSize(content_bounds);
+ if (new_layer_bounds != layer_bounds_) {
+ gfx::Size content_bounds =
+ gfx::ToCeiledSize(gfx::ScaleSize(new_layer_bounds, contents_scale_));
+
tile_size = client_->CalculateTileSize(content_bounds);
+ if (tile_size.IsEmpty()) {
+ layer_bounds_ = gfx::Size();
+ content_bounds = gfx::Size();
+ } else {
+ layer_bounds_ = new_layer_bounds;
+ }
+
+ // The SetLiveTilesRect() method would drop tiles outside the new bounds,
+ // but may do so incorrectly if resizing the tiling causes the number of
+ // tiles in the tiling_data_ to change.
+ gfx::Rect content_rect(content_bounds);
+ int before_left = tiling_data_.TileXIndexFromSrcCoord(live_tiles_rect_.x());
+ int before_top = tiling_data_.TileYIndexFromSrcCoord(live_tiles_rect_.y());
+ int before_right =
+ tiling_data_.TileXIndexFromSrcCoord(live_tiles_rect_.right() - 1);
+ int before_bottom =
+ tiling_data_.TileYIndexFromSrcCoord(live_tiles_rect_.bottom() - 1);
+
+ // The live_tiles_rect_ is clamped to stay within the tiling size as we
+ // change it.
+ live_tiles_rect_.Intersect(content_rect);
+ tiling_data_.SetTilingSize(content_bounds);
+
+ int after_right = -1;
+ int after_bottom = -1;
+ if (!live_tiles_rect_.IsEmpty()) {
+ after_right =
+ tiling_data_.TileXIndexFromSrcCoord(live_tiles_rect_.right() - 1);
+ after_bottom =
+ tiling_data_.TileYIndexFromSrcCoord(live_tiles_rect_.bottom() - 1);
+ }
+
+ // Drop tiles outside the new layer bounds if the layer shrank.
+ for (int i = after_right + 1; i <= before_right; ++i) {
+ for (int j = before_top; j <= before_bottom; ++j) {
+ TileMap::iterator found = tiles_.find(TileMapKey(i, j));
+ if (found == tiles_.end())
+ continue;
+ ReleaseTile(found->second.get(), client_->GetTree());
+ tiles_.erase(found);
+ }
+ }
+ for (int i = before_left; i <= after_right; ++i) {
+ for (int j = after_bottom + 1; j <= before_bottom; ++j) {
+ TileMap::iterator found = tiles_.find(TileMapKey(i, j));
+ if (found == tiles_.end())
+ continue;
+ ReleaseTile(found->second.get(), client_->GetTree());
+ tiles_.erase(found);
+ }
+ }
+
+ // If the layer grew, the live_tiles_rect_ is not changed, but a new row
+ // and/or column of tiles may now exist inside the same live_tiles_rect_.
+ const PictureLayerTiling* twin_tiling = client_->GetTwinTiling(this);
+ if (after_right > before_right) {
+ DCHECK_EQ(after_right, before_right + 1);
+ for (int j = before_top; j <= after_bottom; ++j)
+ CreateTile(after_right, j, twin_tiling);
+ }
+ if (after_bottom > before_bottom) {
+ DCHECK_EQ(after_bottom, before_bottom + 1);
+ for (int i = before_left; i <= before_right; ++i)
+ CreateTile(i, after_bottom, twin_tiling);
+ }
}
if (tile_size != tiling_data_.max_texture_size()) {
PicturePileImpl* pile = client_->GetPile();
for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it)
it->second->set_picture_pile(pile);
+ VerifyLiveTilesRect();
}
void PictureLayerTiling::RemoveTilesInRegion(const Region& layer_region) {
bool recreate_invalidated_tiles) {
std::vector<TileMapKey> new_tile_keys;
gfx::Rect expanded_live_tiles_rect =
- tiling_data_.ExpandRectIgnoringBordersToTileBoundsWithBorders(
- live_tiles_rect_);
+ tiling_data_.ExpandRectIgnoringBordersToTileBounds(live_tiles_rect_);
for (Region::Iterator iter(layer_region); iter.has_rect(); iter.next()) {
gfx::Rect layer_rect = iter.rect();
gfx::Rect content_rect =
gfx::ScaleToEnclosingRect(layer_rect, contents_scale_);
+ // Consider tiles inside the live tiles rect even if only their border
+ // pixels intersect the invalidation. But don't consider tiles outside
+ // the live tiles rect with the same conditions, as they won't exist.
+ int border_pixels = tiling_data_.border_texels();
+ content_rect.Inset(-border_pixels, -border_pixels);
// Avoid needless work by not bothering to invalidate where there aren't
// tiles.
content_rect.Intersect(expanded_live_tiles_rect);
if (content_rect.IsEmpty())
continue;
- bool include_borders = true;
+ // Since the content_rect includes border pixels already, don't include
+ // borders when iterating to avoid double counting them.
+ bool include_borders = false;
for (TilingData::Iterator iter(
&tiling_data_, content_rect, include_borders);
iter;
eviction_tiles_cache_valid_ = false;
TilePriority now_priority(resolution_, TilePriority::NOW, 0);
- float content_to_screen_scale =
- 1.0f / (contents_scale_ * ideal_contents_scale);
+ float content_to_screen_scale = ideal_contents_scale / contents_scale_;
// Assign now priority to all visible tiles.
- bool include_borders = true;
+ bool include_borders = false;
has_visible_rect_tiles_ = false;
for (TilingData::Iterator iter(
&tiling_data_, visible_rect_in_content_space, include_borders);
current_eventually_rect_ = eventually_rect;
}
+void PictureLayerTiling::RemoveTileAt(int i, int j) {
+ TileMapKey key(i, j);
+ TileMap::iterator found = tiles_.find(key);
+ if (found == tiles_.end())
+ return;
+ ReleaseTile(found->second.get(), client_->GetTree());
+ tiles_.erase(found);
+}
+
void PictureLayerTiling::SetLiveTilesRect(
const gfx::Rect& new_live_tiles_rect) {
DCHECK(new_live_tiles_rect.IsEmpty() ||
return;
// Iterate to delete all tiles outside of our new live_tiles rect.
+ PictureLayerTiling* recycled_twin = client_->GetRecycledTwinTiling(this);
for (TilingData::DifferenceIterator iter(&tiling_data_,
live_tiles_rect_,
new_live_tiles_rect);
if (found != tiles_.end()) {
ReleaseTile(found->second.get(), client_->GetTree());
tiles_.erase(found);
+ if (recycled_twin)
+ recycled_twin->RemoveTileAt(iter.index_x(), iter.index_y());
}
}
}
live_tiles_rect_ = new_live_tiles_rect;
+ VerifyLiveTilesRect();
+}
+
+void PictureLayerTiling::VerifyLiveTilesRect() {
+#if DCHECK_IS_ON
+ for (TileMap::iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
+ if (!it->second.get())
+ continue;
+ DCHECK(it->first.first < tiling_data_.num_tiles_x())
+ << this << " " << it->first.first << "," << it->first.second
+ << " num_tiles_x " << tiling_data_.num_tiles_x() << " live_tiles_rect "
+ << live_tiles_rect_.ToString();
+ DCHECK(it->first.second < tiling_data_.num_tiles_y())
+ << this << " " << it->first.first << "," << it->first.second
+ << " num_tiles_y " << tiling_data_.num_tiles_y() << " live_tiles_rect "
+ << live_tiles_rect_.ToString();
+ DCHECK(tiling_data_.TileBounds(it->first.first, it->first.second)
+ .Intersects(live_tiles_rect_))
+ << this << " " << it->first.first << "," << it->first.second
+ << " tile bounds "
+ << tiling_data_.TileBounds(it->first.first, it->first.second).ToString()
+ << " live_tiles_rect " << live_tiles_rect_.ToString();
+ }
+#endif
}
void PictureLayerTiling::DidBecomeRecycled() {
visible_iterator_ = TilingData::Iterator(&tiling_->tiling_data_,
tiling_->current_visible_rect_,
- true /* include_borders */);
+ false /* include_borders */);
if (!visible_iterator_) {
AdvancePhase();
return;
virtual const Region* GetInvalidation() = 0;
virtual const PictureLayerTiling* GetTwinTiling(
const PictureLayerTiling* tiling) const = 0;
+ virtual PictureLayerTiling* GetRecycledTwinTiling(
+ const PictureLayerTiling* tiling) = 0;
virtual size_t GetMaxTilesForInterestArea() const = 0;
virtual float GetSkewportTargetTimeInSeconds() const = 0;
virtual int GetSkewportExtrapolationLimitInContentPixels() const = 0;
const gfx::Size& layer_bounds,
PictureLayerTilingClient* client);
void SetLiveTilesRect(const gfx::Rect& live_tiles_rect);
+ void VerifyLiveTilesRect();
Tile* CreateTile(int i, int j, const PictureLayerTiling* twin_tiling);
+ void RemoveTileAt(int i, int j);
// Computes a skewport. The calculation extrapolates the last visible
// rect and the current visible rect to expand the skewport to where it
client));
}
+ gfx::Rect live_tiles_rect() const { return live_tiles_rect_; }
+
using PictureLayerTiling::ComputeSkewport;
protected:
EXPECT_FALSE(tiling_->TileAt(0, 0));
}
+TEST_F(PictureLayerTilingIteratorTest, CreateMissingTilesStaysInsideLiveRect) {
+ // The tiling has three rows and columns.
+ Initialize(gfx::Size(100, 100), 1, gfx::Size(250, 250));
+ EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_x());
+ EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_y());
+
+ // The live tiles rect is at the very edge of the right-most and
+ // bottom-most tiles. Their border pixels would still be inside the live
+ // tiles rect, but the tiles should not exist just for that.
+ int right = tiling_->TilingDataForTesting().TileBounds(2, 2).x();
+ int bottom = tiling_->TilingDataForTesting().TileBounds(2, 2).y();
+
+ SetLiveRectAndVerifyTiles(gfx::Rect(right, bottom));
+ EXPECT_FALSE(tiling_->TileAt(2, 0));
+ EXPECT_FALSE(tiling_->TileAt(2, 1));
+ EXPECT_FALSE(tiling_->TileAt(2, 2));
+ EXPECT_FALSE(tiling_->TileAt(1, 2));
+ EXPECT_FALSE(tiling_->TileAt(0, 2));
+
+ // Verify CreateMissingTilesInLiveTilesRect respects this.
+ tiling_->CreateMissingTilesInLiveTilesRect();
+ EXPECT_FALSE(tiling_->TileAt(2, 0));
+ EXPECT_FALSE(tiling_->TileAt(2, 1));
+ EXPECT_FALSE(tiling_->TileAt(2, 2));
+ EXPECT_FALSE(tiling_->TileAt(1, 2));
+ EXPECT_FALSE(tiling_->TileAt(0, 2));
+}
+
+TEST_F(PictureLayerTilingIteratorTest, ResizeTilingOverTileBorders) {
+ // The tiling has four rows and three columns.
+ Initialize(gfx::Size(100, 100), 1, gfx::Size(250, 350));
+ EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_x());
+ EXPECT_EQ(4, tiling_->TilingDataForTesting().num_tiles_y());
+
+ // The live tiles rect covers the whole tiling.
+ SetLiveRectAndVerifyTiles(gfx::Rect(250, 350));
+
+ // Tiles in the bottom row and right column exist.
+ EXPECT_TRUE(tiling_->TileAt(2, 0));
+ EXPECT_TRUE(tiling_->TileAt(2, 1));
+ EXPECT_TRUE(tiling_->TileAt(2, 2));
+ EXPECT_TRUE(tiling_->TileAt(2, 3));
+ EXPECT_TRUE(tiling_->TileAt(1, 3));
+ EXPECT_TRUE(tiling_->TileAt(0, 3));
+
+ int right = tiling_->TilingDataForTesting().TileBounds(2, 2).x();
+ int bottom = tiling_->TilingDataForTesting().TileBounds(2, 3).y();
+
+ // Shrink the tiling so that the last tile row/column is entirely in the
+ // border pixels of the interior tiles. That row/column is removed.
+ Region invalidation;
+ tiling_->UpdateTilesToCurrentPile(invalidation,
+ gfx::Size(right + 1, bottom + 1));
+ EXPECT_EQ(2, tiling_->TilingDataForTesting().num_tiles_x());
+ EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_y());
+
+ // The live tiles rect was clamped to the pile size.
+ EXPECT_EQ(gfx::Rect(right + 1, bottom + 1), tiling_->live_tiles_rect());
+
+ // Since the row/column is gone, the tiles should be gone too.
+ EXPECT_FALSE(tiling_->TileAt(2, 0));
+ EXPECT_FALSE(tiling_->TileAt(2, 1));
+ EXPECT_FALSE(tiling_->TileAt(2, 2));
+ EXPECT_FALSE(tiling_->TileAt(2, 3));
+ EXPECT_FALSE(tiling_->TileAt(1, 3));
+ EXPECT_FALSE(tiling_->TileAt(0, 3));
+
+ // Growing outside the current right/bottom tiles border pixels should create
+ // the tiles again, even though the live rect has not changed size.
+ tiling_->UpdateTilesToCurrentPile(invalidation,
+ gfx::Size(right + 2, bottom + 2));
+ EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_x());
+ EXPECT_EQ(4, tiling_->TilingDataForTesting().num_tiles_y());
+
+ // Not changed.
+ EXPECT_EQ(gfx::Rect(right + 1, bottom + 1), tiling_->live_tiles_rect());
+
+ // The last row/column tiles are inside the live tiles rect.
+ EXPECT_TRUE(gfx::Rect(right + 1, bottom + 1).Intersects(
+ tiling_->TilingDataForTesting().TileBounds(2, 0)));
+ EXPECT_TRUE(gfx::Rect(right + 1, bottom + 1).Intersects(
+ tiling_->TilingDataForTesting().TileBounds(0, 3)));
+
+ EXPECT_TRUE(tiling_->TileAt(2, 0));
+ EXPECT_TRUE(tiling_->TileAt(2, 1));
+ EXPECT_TRUE(tiling_->TileAt(2, 2));
+ EXPECT_TRUE(tiling_->TileAt(2, 3));
+ EXPECT_TRUE(tiling_->TileAt(1, 3));
+ EXPECT_TRUE(tiling_->TileAt(0, 3));
+}
+
+TEST_F(PictureLayerTilingIteratorTest, ResizeLiveTileRectOverTileBorders) {
+ // The tiling has three rows and columns.
+ Initialize(gfx::Size(100, 100), 1, gfx::Size(250, 350));
+ EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_x());
+ EXPECT_EQ(4, tiling_->TilingDataForTesting().num_tiles_y());
+
+ // The live tiles rect covers the whole tiling.
+ SetLiveRectAndVerifyTiles(gfx::Rect(250, 350));
+
+ // Tiles in the bottom row and right column exist.
+ EXPECT_TRUE(tiling_->TileAt(2, 0));
+ EXPECT_TRUE(tiling_->TileAt(2, 1));
+ EXPECT_TRUE(tiling_->TileAt(2, 2));
+ EXPECT_TRUE(tiling_->TileAt(2, 3));
+ EXPECT_TRUE(tiling_->TileAt(1, 3));
+ EXPECT_TRUE(tiling_->TileAt(0, 3));
+
+ // Shrink the live tiles rect to the very edge of the right-most and
+ // bottom-most tiles. Their border pixels would still be inside the live
+ // tiles rect, but the tiles should not exist just for that.
+ int right = tiling_->TilingDataForTesting().TileBounds(2, 3).x();
+ int bottom = tiling_->TilingDataForTesting().TileBounds(2, 3).y();
+
+ SetLiveRectAndVerifyTiles(gfx::Rect(right, bottom));
+ EXPECT_FALSE(tiling_->TileAt(2, 0));
+ EXPECT_FALSE(tiling_->TileAt(2, 1));
+ EXPECT_FALSE(tiling_->TileAt(2, 2));
+ EXPECT_FALSE(tiling_->TileAt(2, 3));
+ EXPECT_FALSE(tiling_->TileAt(1, 3));
+ EXPECT_FALSE(tiling_->TileAt(0, 3));
+
+ // Including the bottom row and right column again, should create the tiles.
+ SetLiveRectAndVerifyTiles(gfx::Rect(right + 1, bottom + 1));
+ EXPECT_TRUE(tiling_->TileAt(2, 0));
+ EXPECT_TRUE(tiling_->TileAt(2, 1));
+ EXPECT_TRUE(tiling_->TileAt(2, 2));
+ EXPECT_TRUE(tiling_->TileAt(2, 3));
+ EXPECT_TRUE(tiling_->TileAt(1, 2));
+ EXPECT_TRUE(tiling_->TileAt(0, 2));
+
+ // Shrink the live tiles rect to the very edge of the left-most and
+ // top-most tiles. Their border pixels would still be inside the live
+ // tiles rect, but the tiles should not exist just for that.
+ int left = tiling_->TilingDataForTesting().TileBounds(0, 0).right();
+ int top = tiling_->TilingDataForTesting().TileBounds(0, 0).bottom();
+
+ SetLiveRectAndVerifyTiles(gfx::Rect(left, top, 250 - left, 350 - top));
+ EXPECT_FALSE(tiling_->TileAt(0, 3));
+ EXPECT_FALSE(tiling_->TileAt(0, 2));
+ EXPECT_FALSE(tiling_->TileAt(0, 1));
+ EXPECT_FALSE(tiling_->TileAt(0, 0));
+ EXPECT_FALSE(tiling_->TileAt(1, 0));
+ EXPECT_FALSE(tiling_->TileAt(2, 0));
+
+ // Including the top row and left column again, should create the tiles.
+ SetLiveRectAndVerifyTiles(
+ gfx::Rect(left - 1, top - 1, 250 - left, 350 - top));
+ EXPECT_TRUE(tiling_->TileAt(0, 3));
+ EXPECT_TRUE(tiling_->TileAt(0, 2));
+ EXPECT_TRUE(tiling_->TileAt(0, 1));
+ EXPECT_TRUE(tiling_->TileAt(0, 0));
+ EXPECT_TRUE(tiling_->TileAt(1, 0));
+ EXPECT_TRUE(tiling_->TileAt(2, 0));
+}
+
+TEST_F(PictureLayerTilingIteratorTest, ResizeLiveTileRectOverSameTiles) {
+ // The tiling has four rows and three columns.
+ Initialize(gfx::Size(100, 100), 1, gfx::Size(250, 350));
+ EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_x());
+ EXPECT_EQ(4, tiling_->TilingDataForTesting().num_tiles_y());
+
+ // The live tiles rect covers the whole tiling.
+ SetLiveRectAndVerifyTiles(gfx::Rect(250, 350));
+
+ // All tiles exist.
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 4; ++j)
+ EXPECT_TRUE(tiling_->TileAt(i, j)) << i << "," << j;
+ }
+
+ // Shrink the live tiles rect, but still cover all the tiles.
+ SetLiveRectAndVerifyTiles(gfx::Rect(1, 1, 249, 349));
+
+ // All tiles still exist.
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 4; ++j)
+ EXPECT_TRUE(tiling_->TileAt(i, j)) << i << "," << j;
+ }
+
+ // Grow the live tiles rect, but still cover all the same tiles.
+ SetLiveRectAndVerifyTiles(gfx::Rect(0, 0, 250, 350));
+
+ // All tiles still exist.
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 4; ++j)
+ EXPECT_TRUE(tiling_->TileAt(i, j)) << i << "," << j;
+ }
+}
+
TEST_F(PictureLayerTilingIteratorTest, ResizeOverBorderPixelsDeletesTiles) {
// Verifies that a resize with invalidation for newly exposed pixels will
// deletes tiles that intersect that invalidation.
Tile* tile = tiling->TileAt(i, j);
TilePriority priority = tile->priority(ACTIVE_TREE);
- if (viewport_in_content_space.Intersects(tile->content_rect())) {
+ gfx::Rect tile_rect = tiling->TilingDataForTesting().TileBounds(i, j);
+ if (viewport_in_content_space.Intersects(tile_rect)) {
EXPECT_EQ(TilePriority::NOW, priority.priority_bin);
EXPECT_FLOAT_EQ(0.f, priority.distance_to_visible);
have_now = true;
- } else if (soon_rect_in_content_space.Intersects(tile->content_rect())) {
+ } else if (soon_rect_in_content_space.Intersects(tile_rect)) {
EXPECT_EQ(TilePriority::SOON, priority.priority_bin);
have_soon = true;
} else {
Tile* tile = tiling->TileAt(i, j);
TilePriority priority = tile->priority(ACTIVE_TREE);
- if (viewport_in_content_space.Intersects(tile->content_rect())) {
+ gfx::Rect tile_rect = tiling->TilingDataForTesting().TileBounds(i, j);
+ if (viewport_in_content_space.Intersects(tile_rect)) {
EXPECT_EQ(TilePriority::NOW, priority.priority_bin) << "i: " << i
<< " j: " << j;
EXPECT_FLOAT_EQ(0.f, priority.distance_to_visible) << "i: " << i
<< " j: " << j;
have_now = true;
- } else if (skewport.Intersects(tile->content_rect()) ||
- soon_rect_in_content_space.Intersects(tile->content_rect())) {
+ } else if (skewport.Intersects(tile_rect) ||
+ soon_rect_in_content_space.Intersects(tile_rect)) {
EXPECT_EQ(TilePriority::SOON, priority.priority_bin) << "i: " << i
<< " j: " << j;
EXPECT_GT(priority.distance_to_visible, 0.f) << "i: " << i
EXPECT_FLOAT_EQ(28.f, priority.distance_to_visible);
priority = tiling->TileAt(3, 4)->priority(ACTIVE_TREE);
- EXPECT_FLOAT_EQ(0.f, priority.distance_to_visible);
+ EXPECT_FLOAT_EQ(4.f, priority.distance_to_visible);
// Change the underlying layer scale.
tiling->UpdateTilePriorities(
ACTIVE_TREE, viewport, 2.0f, 3.0, NULL, NULL, gfx::Transform());
priority = tiling->TileAt(5, 1)->priority(ACTIVE_TREE);
- EXPECT_FLOAT_EQ(34.f, priority.distance_to_visible);
+ EXPECT_FLOAT_EQ(136.f, priority.distance_to_visible);
priority = tiling->TileAt(2, 5)->priority(ACTIVE_TREE);
- EXPECT_FLOAT_EQ(14.f, priority.distance_to_visible);
+ EXPECT_FLOAT_EQ(56.f, priority.distance_to_visible);
priority = tiling->TileAt(3, 4)->priority(ACTIVE_TREE);
- EXPECT_FLOAT_EQ(0.f, priority.distance_to_visible);
+ EXPECT_FLOAT_EQ(8.f, priority.distance_to_visible);
+
+ // Test additional scales.
+ tiling = TestablePictureLayerTiling::Create(0.2f, layer_bounds, &client);
+ tiling->UpdateTilePriorities(
+ ACTIVE_TREE, viewport, 1.0f, 4.0, NULL, NULL, gfx::Transform());
+
+ priority = tiling->TileAt(5, 1)->priority(ACTIVE_TREE);
+ EXPECT_FLOAT_EQ(110.f, priority.distance_to_visible);
+
+ priority = tiling->TileAt(2, 5)->priority(ACTIVE_TREE);
+ EXPECT_FLOAT_EQ(70.f, priority.distance_to_visible);
+
+ priority = tiling->TileAt(3, 4)->priority(ACTIVE_TREE);
+ EXPECT_FLOAT_EQ(60.f, priority.distance_to_visible);
+
+ tiling->UpdateTilePriorities(
+ ACTIVE_TREE, viewport, 0.5f, 5.0, NULL, NULL, gfx::Transform());
+
+ priority = tiling->TileAt(5, 1)->priority(ACTIVE_TREE);
+ EXPECT_FLOAT_EQ(55.f, priority.distance_to_visible);
+
+ priority = tiling->TileAt(2, 5)->priority(ACTIVE_TREE);
+ EXPECT_FLOAT_EQ(35.f, priority.distance_to_visible);
+
+ priority = tiling->TileAt(3, 4)->priority(ACTIVE_TREE);
+ EXPECT_FLOAT_EQ(30.f, priority.distance_to_visible);
}
TEST(PictureLayerTilingTest, ExpandRectEqual) {
tiles.clear();
}
+TEST(PictureLayerTilingTest, RecycledTilesCleared) {
+ // This test performs the following:
+ // Setup:
+ // - Two tilings, one active one recycled with all tiles shared.
+ // Procedure:
+ // - Viewport moves somewhere far away and active tiling clears tiles.
+ // - Viewport moves back and a new active tiling tile is created.
+ // Result:
+ // - Recycle tiling does _not_ have the tile in the same location (thus it
+ // will be shared next time a pending tiling is created).
+
+ FakePictureLayerTilingClient client;
+ scoped_ptr<TestablePictureLayerTiling> tiling;
+
+ client.SetTileSize(gfx::Size(100, 100));
+ client.set_tree(ACTIVE_TREE);
+ client.set_max_tiles_for_interest_area(10);
+ tiling = TestablePictureLayerTiling::Create(1.0f, // contents_scale
+ gfx::Size(10000, 10000),
+ &client);
+ // Create all tiles on this tiling.
+ tiling->UpdateTilePriorities(ACTIVE_TREE,
+ gfx::Rect(0, 0, 100, 100),
+ 1.0f,
+ 1.0f,
+ NULL, // occlusion tracker
+ NULL, // render target
+ gfx::Transform()); // draw transform
+
+ FakePictureLayerTilingClient second_client;
+ second_client.SetTileSize(gfx::Size(100, 100));
+ second_client.set_tree(PENDING_TREE);
+ second_client.set_twin_tiling(tiling.get());
+ second_client.set_max_tiles_for_interest_area(10);
+
+ scoped_ptr<TestablePictureLayerTiling> second_tiling;
+ second_tiling = TestablePictureLayerTiling::Create(1.0f, // contents_scale
+ gfx::Size(10000, 10000),
+ &second_client);
+
+ // Create all tiles on the second tiling. All tiles should be shared.
+ second_tiling->UpdateTilePriorities(ACTIVE_TREE,
+ gfx::Rect(0, 0, 100, 100),
+ 1.0f,
+ 1.0f,
+ NULL, // occlusion tracker
+ NULL, // render target
+ gfx::Transform()); // draw transform
+
+ // Verify that tiles exist and are shared.
+ ASSERT_TRUE(tiling->TileAt(0, 0));
+ ASSERT_EQ(tiling->TileAt(0, 0), second_tiling->TileAt(0, 0));
+
+ // Set the second tiling as recycled.
+ client.set_twin_tiling(NULL);
+ client.set_recycled_twin_tiling(second_tiling.get());
+ second_client.set_twin_tiling(NULL);
+
+ // Move the viewport far away from the (0, 0) tile.
+ tiling->UpdateTilePriorities(ACTIVE_TREE,
+ gfx::Rect(9000, 9000, 100, 100),
+ 1.0f,
+ 2.0,
+ NULL, // occlusion tracker
+ NULL, // render target
+ gfx::Transform()); // draw transform
+ // Ensure the tile was deleted.
+ EXPECT_FALSE(tiling->TileAt(0, 0));
+
+ // Move the viewport back to (0, 0) tile.
+ tiling->UpdateTilePriorities(ACTIVE_TREE,
+ gfx::Rect(0, 0, 100, 100),
+ 1.0f,
+ 3.0,
+ NULL, // occlusion tracker
+ NULL, // render target
+ gfx::Transform()); // draw transform
+
+ // Ensure that we now have a tile here, but the recycle tiling does not.
+ EXPECT_TRUE(tiling->TileAt(0, 0));
+ EXPECT_FALSE(second_tiling->TileAt(0, 0));
+}
+
} // namespace
} // namespace cc
gfx::Rect old_tiling_rect_over_tiles =
tiling_.ExpandRectToTileBounds(gfx::Rect(old_tiling_size));
if (min_toss_x < tiling_.num_tiles_x()) {
- int unrecorded_left = std::max(tiling_.TilePositionX(min_toss_x),
- interest_rect_over_tiles.right());
+ // The bounds which we want to invalidate are the tiles along the old
+ // edge of the pile. We'll call this bounding box the OLD EDGE RECT.
+ //
+ // In the picture below, the old edge rect would be the bounding box
+ // of tiles {h,i,j}. |min_toss_x| would be equal to the horizontal index
+ // of the same tiles.
+ //
+ // old pile edge-v new pile edge-v
+ // ---------------+ - - - - - - - -+
+ // mmppssvvyybbeeh|h .
+ // mmppssvvyybbeeh|h .
+ // nnqqttwwzzccffi|i .
+ // nnqqttwwzzccffi|i .
+ // oorruuxxaaddggj|j .
+ // oorruuxxaaddggj|j .
+ // ---------------+ - - - - - - - -+ <- old pile edge
+ // .
+ // - - - - - - - - - - - - - - - -+ <- new pile edge
+ //
+ // If you were to slide a vertical beam from the left edge of the
+ // old edge rect toward the right, it would either hit the right edge
+ // of the old edge rect, or the interest rect (expanded to the bounds
+ // of the tiles it touches). The same is true for a beam parallel to
+ // any of the four edges, sliding accross the old edge rect. We use
+ // the union of these four rectangles generated by these beams to
+ // determine which part of the old edge rect is outside of the expanded
+ // interest rect.
+ //
+ // Case 1: Intersect rect is outside the old edge rect. It can be
+ // either on the left or the right. The |left_rect| and |right_rect|,
+ // cover this case, one will be empty and one will cover the full
+ // old edge rect. In the picture below, |left_rect| would cover the
+ // old edge rect, and |right_rect| would be empty.
+ // +----------------------+ |^^^^^^^^^^^^^^^|
+ // |===> OLD EDGE RECT | | |
+ // |===> | | INTEREST RECT |
+ // |===> | | |
+ // |===> | | |
+ // +----------------------+ |vvvvvvvvvvvvvvv|
+ //
+ // Case 2: Interest rect is inside the old edge rect. It will always
+ // fill the entire old edge rect horizontally since the old edge rect
+ // is a single tile wide, and the interest rect has been expanded to the
+ // bounds of the tiles it touches. In this case the |left_rect| and
+ // |right_rect| will be empty, but the case is handled by the |top_rect|
+ // and |bottom_rect|. In the picture below, neither the |top_rect| nor
+ // |bottom_rect| would empty, they would each cover the area of the old
+ // edge rect outside the expanded interest rect.
+ // +-----------------+
+ // |:::::::::::::::::|
+ // |:::::::::::::::::|
+ // |vvvvvvvvvvvvvvvvv|
+ // | |
+ // +-----------------+
+ // | INTEREST RECT |
+ // | |
+ // +-----------------+
+ // | |
+ // | OLD EDGE RECT |
+ // +-----------------+
+ //
+ // Lastly, we need to consider tiles inside the expanded interest rect.
+ // For those tiles, we want to invalidate exactly the newly exposed
+ // pixels. In the picture below the tiles in the old edge rect have been
+ // resized and the area covered by periods must be invalidated. The
+ // |exposed_rect| will cover exactly that area.
+ // v-old pile edge
+ // +---------+-------+
+ // | ........|
+ // | ........|
+ // | OLD EDGE.RECT..|
+ // | ........|
+ // | ........|
+ // | ........|
+ // | ........|
+ // | ........|
+ // | ........|
+ // +---------+-------+
+
+ int left = tiling_.TilePositionX(min_toss_x);
+ int right = left + tiling_.TileSizeX(min_toss_x);
+ int top = old_tiling_rect_over_tiles.y();
+ int bottom = old_tiling_rect_over_tiles.bottom();
+
+ int left_until = std::min(interest_rect_over_tiles.x(), right);
+ int right_until = std::max(interest_rect_over_tiles.right(), left);
+ int top_until = std::min(interest_rect_over_tiles.y(), bottom);
+ int bottom_until = std::max(interest_rect_over_tiles.bottom(), top);
+
int exposed_left = old_tiling_size.width();
- int left = std::min(unrecorded_left, exposed_left);
- int tile_right =
- tiling_.TilePositionX(min_toss_x) + tiling_.TileSizeX(min_toss_x);
- int exposed_right = tiling_size().width();
- int right = std::min(tile_right, exposed_right);
- gfx::Rect right_side(left,
- old_tiling_rect_over_tiles.y(),
- right - left,
- old_tiling_rect_over_tiles.height());
- resize_invalidation.Union(right_side);
+ int exposed_left_until = tiling_size().width();
+ int exposed_top = top;
+ int exposed_bottom = tiling_size().height();
+ DCHECK_GE(exposed_left, left);
+
+ gfx::Rect left_rect(left, top, left_until - left, bottom - top);
+ gfx::Rect right_rect(right_until, top, right - right_until, bottom - top);
+ gfx::Rect top_rect(left, top, right - left, top_until - top);
+ gfx::Rect bottom_rect(
+ left, bottom_until, right - left, bottom - bottom_until);
+ gfx::Rect exposed_rect(exposed_left,
+ exposed_top,
+ exposed_left_until - exposed_left,
+ exposed_bottom - exposed_top);
+ resize_invalidation.Union(left_rect);
+ resize_invalidation.Union(right_rect);
+ resize_invalidation.Union(top_rect);
+ resize_invalidation.Union(bottom_rect);
+ resize_invalidation.Union(exposed_rect);
}
if (min_toss_y < tiling_.num_tiles_y()) {
- int unrecorded_top = std::max(tiling_.TilePositionY(min_toss_y),
- interest_rect_over_tiles.bottom());
+ // The same thing occurs here as in the case above, but the invalidation
+ // rect is the bounding box around the bottom row of tiles in the old
+ // pile. This would be tiles {o,r,u,x,a,d,g,j} in the above picture.
+
+ int top = tiling_.TilePositionY(min_toss_y);
+ int bottom = top + tiling_.TileSizeY(min_toss_y);
+ int left = old_tiling_rect_over_tiles.x();
+ int right = old_tiling_rect_over_tiles.right();
+
+ int top_until = std::min(interest_rect_over_tiles.y(), bottom);
+ int bottom_until = std::max(interest_rect_over_tiles.bottom(), top);
+ int left_until = std::min(interest_rect_over_tiles.x(), right);
+ int right_until = std::max(interest_rect_over_tiles.right(), left);
+
int exposed_top = old_tiling_size.height();
- int top = std::min(unrecorded_top, exposed_top);
- int tile_bottom =
- tiling_.TilePositionY(min_toss_y) + tiling_.TileSizeY(min_toss_y);
- int exposed_bottom = tiling_size().height();
- int bottom = std::min(tile_bottom, exposed_bottom);
- gfx::Rect bottom_side(old_tiling_rect_over_tiles.x(),
- top,
- old_tiling_rect_over_tiles.width(),
- bottom - top);
- resize_invalidation.Union(bottom_side);
+ int exposed_top_until = tiling_size().height();
+ int exposed_left = left;
+ int exposed_right = tiling_size().width();
+ DCHECK_GE(exposed_top, top);
+
+ gfx::Rect left_rect(left, top, left_until - left, bottom - top);
+ gfx::Rect right_rect(right_until, top, right - right_until, bottom - top);
+ gfx::Rect top_rect(left, top, right - left, top_until - top);
+ gfx::Rect bottom_rect(
+ left, bottom_until, right - left, bottom - bottom_until);
+ gfx::Rect exposed_rect(exposed_left,
+ exposed_top,
+ exposed_right - exposed_left,
+ exposed_top_until - exposed_top);
+ resize_invalidation.Union(left_rect);
+ resize_invalidation.Union(right_rect);
+ resize_invalidation.Union(top_rect);
+ resize_invalidation.Union(bottom_rect);
+ resize_invalidation.Union(exposed_rect);
}
}
contents_fill_bounds_completely_(false),
show_debug_picture_borders_(false),
clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
- has_any_recordings_(false) {
+ has_any_recordings_(false),
+ is_mask_(false) {
tiling_.SetMaxTextureSize(gfx::Size(kBasePictureSize, kBasePictureSize));
tile_grid_info_.fTileInterval.setEmpty();
tile_grid_info_.fMargin.setEmpty();
contents_fill_bounds_completely_(other->contents_fill_bounds_completely_),
show_debug_picture_borders_(other->show_debug_picture_borders_),
clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_),
- has_any_recordings_(other->has_any_recordings_) {}
+ has_any_recordings_(other->has_any_recordings_),
+ is_mask_(other->is_mask_) {
+}
PicturePileBase::PicturePileBase(const PicturePileBase* other,
unsigned thread_index)
contents_fill_bounds_completely_(other->contents_fill_bounds_completely_),
show_debug_picture_borders_(other->show_debug_picture_borders_),
clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_),
- has_any_recordings_(other->has_any_recordings_) {
+ has_any_recordings_(other->has_any_recordings_),
+ is_mask_(other->is_mask_) {
for (PictureMap::const_iterator it = other->picture_map_.begin();
it != other->picture_map_.end();
++it) {
// If this pile contains any valid recordings. May have false positives.
bool HasRecordings() const { return has_any_recordings_; }
+ void set_is_mask(bool is_mask) { is_mask_ = is_mask; }
+ bool is_mask() const { return is_mask_; }
+
static void ComputeTileGridInfo(const gfx::Size& tile_grid_size,
SkTileGridFactory::TileGridInfo* info);
// A hint about whether there are any recordings. This may be a false
// positive.
bool has_any_recordings_;
+ bool is_mask_;
private:
void SetBufferPixels(int buffer_pixels);
virtual ~TestPicturePile() {}
};
-class PicturePileTest : public testing::Test {
+class PicturePileTestBase {
public:
- PicturePileTest()
+ PicturePileTestBase()
: pile_(new TestPicturePile()),
background_color_(SK_ColorBLUE),
min_scale_(0.125),
bool contents_opaque_;
};
+class PicturePileTest : public PicturePileTestBase, public testing::Test {};
+
TEST_F(PicturePileTest, SmallInvalidateInflated) {
// Invalidate something inside a tile.
Region invalidate_rect(gfx::Rect(50, 50, 1, 1));
EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
}
-TEST_F(PicturePileTest, ResizePileOutsideInterestRect) {
+enum Corner {
+ TOP_LEFT,
+ TOP_RIGHT,
+ BOTTOM_LEFT,
+ BOTTOM_RIGHT,
+};
+
+class PicturePileResizeCornerTest : public PicturePileTestBase,
+ public testing::TestWithParam<Corner> {
+ protected:
+ static gfx::Rect CornerSinglePixelRect(Corner corner, const gfx::Size& s) {
+ switch (corner) {
+ case TOP_LEFT:
+ return gfx::Rect(0, 0, 1, 1);
+ case TOP_RIGHT:
+ return gfx::Rect(s.width() - 1, 0, 1, 1);
+ case BOTTOM_LEFT:
+ return gfx::Rect(0, s.height() - 1, 1, 1);
+ case BOTTOM_RIGHT:
+ return gfx::Rect(s.width() - 1, s.height() - 1, 1, 1);
+ }
+ NOTREACHED();
+ return gfx::Rect();
+ }
+};
+
+TEST_P(PicturePileResizeCornerTest, ResizePileOutsideInterestRect) {
+ Corner corner = GetParam();
+
// This size chosen to be larger than the interest rect size, which is
// at least kPixelDistanceToRecord * 2 in each dimension.
int tile_size = 100000;
- gfx::Size base_tiling_size(5 * tile_size, 5 * tile_size);
- gfx::Size grow_down_tiling_size(5 * tile_size, 7 * tile_size);
- gfx::Size grow_right_tiling_size(7 * tile_size, 5 * tile_size);
- gfx::Size grow_both_tiling_size(7 * tile_size, 7 * tile_size);
+ // The small number subtracted keeps the last tile in each axis larger than
+ // the interest rect also.
+ int offset = -100;
+ gfx::Size base_tiling_size(6 * tile_size + offset, 6 * tile_size + offset);
+ gfx::Size grow_down_tiling_size(6 * tile_size + offset,
+ 8 * tile_size + offset);
+ gfx::Size grow_right_tiling_size(8 * tile_size + offset,
+ 6 * tile_size + offset);
+ gfx::Size grow_both_tiling_size(8 * tile_size + offset,
+ 8 * tile_size + offset);
Region invalidation;
Region expected_invalidation;
}
UpdateAndExpandInvalidation(
- &invalidation, grow_down_tiling_size, gfx::Rect(1, 1));
+ &invalidation,
+ grow_down_tiling_size,
+ CornerSinglePixelRect(corner, grow_down_tiling_size));
// We should have lost the recordings in the bottom row.
EXPECT_EQ(6, pile_->tiling().num_tiles_x());
EXPECT_EQ(8, pile_->tiling().num_tiles_y());
- for (int i = 0; i < pile_->tiling().num_tiles_x(); ++i) {
- for (int j = 0; j < pile_->tiling().num_tiles_y(); ++j) {
+ for (int i = 0; i < 6; ++i) {
+ for (int j = 0; j < 6; ++j) {
TestPicturePile::PictureMapKey key(i, j);
TestPicturePile::PictureMap& map = pile_->picture_map();
TestPicturePile::PictureMap::iterator it = map.find(key);
}
}
- // We invalidated the old bottom row.
- expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(0, 5),
- pile_->tiling().TileBounds(5, 5));
+ // We invalidated all new pixels in the recording.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size),
+ gfx::Rect(base_tiling_size));
+ // But the new pixels don't cover the whole bottom row.
+ gfx::Rect bottom_row = gfx::UnionRects(pile_->tiling().TileBounds(0, 5),
+ pile_->tiling().TileBounds(5, 5));
+ EXPECT_FALSE(expected_invalidation.Contains(bottom_row));
+ // We invalidated the entire old bottom row.
+ expected_invalidation.Union(bottom_row);
EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
invalidation.Clear();
UpdateWholePile();
- UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1));
+ UpdateAndExpandInvalidation(&invalidation,
+ base_tiling_size,
+ CornerSinglePixelRect(corner, base_tiling_size));
// We should have lost the recordings that are now outside the tiling only.
EXPECT_EQ(6, pile_->tiling().num_tiles_x());
UpdateWholePile();
UpdateAndExpandInvalidation(
- &invalidation, grow_right_tiling_size, gfx::Rect(1, 1));
+ &invalidation,
+ grow_right_tiling_size,
+ CornerSinglePixelRect(corner, grow_right_tiling_size));
// We should have lost the recordings in the right column.
EXPECT_EQ(8, pile_->tiling().num_tiles_x());
EXPECT_EQ(6, pile_->tiling().num_tiles_y());
- for (int i = 0; i < pile_->tiling().num_tiles_x(); ++i) {
- for (int j = 0; j < pile_->tiling().num_tiles_y(); ++j) {
+ for (int i = 0; i < 6; ++i) {
+ for (int j = 0; j < 6; ++j) {
TestPicturePile::PictureMapKey key(i, j);
TestPicturePile::PictureMap& map = pile_->picture_map();
TestPicturePile::PictureMap::iterator it = map.find(key);
}
}
- // We invalidated the old right column.
- expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
- pile_->tiling().TileBounds(5, 5));
+ // We invalidated all new pixels in the recording.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size),
+ gfx::Rect(base_tiling_size));
+ // But the new pixels don't cover the whole right_column.
+ gfx::Rect right_column = gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
+ pile_->tiling().TileBounds(5, 5));
+ EXPECT_FALSE(expected_invalidation.Contains(right_column));
+ // We invalidated the entire old right column.
+ expected_invalidation.Union(right_column);
EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
invalidation.Clear();
UpdateWholePile();
- UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1));
+ UpdateAndExpandInvalidation(&invalidation,
+ base_tiling_size,
+ CornerSinglePixelRect(corner, base_tiling_size));
// We should have lost the recordings that are now outside the tiling only.
EXPECT_EQ(6, pile_->tiling().num_tiles_x());
UpdateWholePile();
UpdateAndExpandInvalidation(
- &invalidation, grow_both_tiling_size, gfx::Rect(1, 1));
+ &invalidation,
+ grow_both_tiling_size,
+ CornerSinglePixelRect(corner, grow_both_tiling_size));
// We should have lost the recordings in the right column and bottom row.
EXPECT_EQ(8, pile_->tiling().num_tiles_x());
EXPECT_EQ(8, pile_->tiling().num_tiles_y());
- for (int i = 0; i < pile_->tiling().num_tiles_x(); ++i) {
- for (int j = 0; j < pile_->tiling().num_tiles_y(); ++j) {
+ for (int i = 0; i < 6; ++i) {
+ for (int j = 0; j < 6; ++j) {
TestPicturePile::PictureMapKey key(i, j);
TestPicturePile::PictureMap& map = pile_->picture_map();
TestPicturePile::PictureMap::iterator it = map.find(key);
}
}
- // We invalidated the old right column and the old bottom row.
- expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
- pile_->tiling().TileBounds(5, 5));
- expected_invalidation.Union(gfx::UnionRects(
- pile_->tiling().TileBounds(0, 5), pile_->tiling().TileBounds(5, 5)));
+ // We invalidated all new pixels in the recording.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size),
+ gfx::Rect(base_tiling_size));
+ // But the new pixels don't cover the whole right_column.
+ Region right_column_and_bottom_row =
+ UnionRegions(gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
+ pile_->tiling().TileBounds(5, 5)),
+ gfx::UnionRects(pile_->tiling().TileBounds(0, 5),
+ pile_->tiling().TileBounds(5, 5)));
+ EXPECT_FALSE(expected_invalidation.Contains(right_column_and_bottom_row));
+ // We invalidated the entire old right column and the old bottom row.
+ expected_invalidation.Union(right_column_and_bottom_row);
EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
invalidation.Clear();
invalidation.Clear();
}
-TEST_F(PicturePileTest, SmallResizePileOutsideInterestRect) {
+TEST_P(PicturePileResizeCornerTest, SmallResizePileOutsideInterestRect) {
+ Corner corner = GetParam();
+
// This size chosen to be larger than the interest rect size, which is
// at least kPixelDistanceToRecord * 2 in each dimension.
int tile_size = 100000;
- gfx::Size base_tiling_size(5 * tile_size, 5 * tile_size);
- gfx::Size grow_down_tiling_size(5 * tile_size, 5 * tile_size + 5);
- gfx::Size grow_right_tiling_size(5 * tile_size + 5, 5 * tile_size);
- gfx::Size grow_both_tiling_size(5 * tile_size + 5, 5 * tile_size + 5);
+ // The small number subtracted keeps the last tile in each axis larger than
+ // the interest rect also.
+ int offset = -100;
+ gfx::Size base_tiling_size(6 * tile_size + offset, 6 * tile_size + offset);
+ gfx::Size grow_down_tiling_size(6 * tile_size + offset,
+ 6 * tile_size + offset + 5);
+ gfx::Size grow_right_tiling_size(6 * tile_size + offset + 5,
+ 6 * tile_size + offset);
+ gfx::Size grow_both_tiling_size(6 * tile_size + offset + 5,
+ 6 * tile_size + offset + 5);
Region invalidation;
Region expected_invalidation;
}
UpdateAndExpandInvalidation(
- &invalidation, grow_down_tiling_size, gfx::Rect(1, 1));
+ &invalidation,
+ grow_down_tiling_size,
+ CornerSinglePixelRect(corner, grow_down_tiling_size));
- // We should have lost the recordings in the bottom row.
+ // We should have lost the recordings in the bottom row that do not intersect
+ // the interest rect.
EXPECT_EQ(6, pile_->tiling().num_tiles_x());
EXPECT_EQ(6, pile_->tiling().num_tiles_y());
for (int i = 0; i < pile_->tiling().num_tiles_x(); ++i) {
TestPicturePile::PictureMapKey key(i, j);
TestPicturePile::PictureMap& map = pile_->picture_map();
TestPicturePile::PictureMap::iterator it = map.find(key);
- EXPECT_EQ(j < 5, it != map.end() && it->second.GetPicture());
+ bool expect_tile;
+ switch (corner) {
+ case TOP_LEFT:
+ case TOP_RIGHT:
+ expect_tile = j < 5;
+ break;
+ case BOTTOM_LEFT:
+ // The interest rect in the bottom left tile means we'll record it.
+ expect_tile = j < 5 || (j == 5 && i == 0);
+ break;
+ case BOTTOM_RIGHT:
+ // The interest rect in the bottom right tile means we'll record it.
+ expect_tile = j < 5 || (j == 5 && i == 5);
+ break;
+ }
+ EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture());
}
}
- // We invalidated the bottom row.
- expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(0, 5),
- pile_->tiling().TileBounds(5, 5));
+ // We invalidated the bottom row outside the new interest rect. The tile that
+ // insects the interest rect in invalidated only on its new pixels.
+ switch (corner) {
+ case TOP_LEFT:
+ case TOP_RIGHT:
+ expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(0, 5),
+ pile_->tiling().TileBounds(5, 5));
+ break;
+ case BOTTOM_LEFT:
+ expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(1, 5),
+ pile_->tiling().TileBounds(5, 5));
+ expected_invalidation.Union(SubtractRects(
+ pile_->tiling().TileBounds(0, 5), gfx::Rect(base_tiling_size)));
+ break;
+ case BOTTOM_RIGHT:
+ expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(0, 5),
+ pile_->tiling().TileBounds(4, 5));
+ expected_invalidation.Union(SubtractRects(
+ pile_->tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size)));
+ break;
+ }
EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
invalidation.Clear();
UpdateWholePile();
- UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1));
+ UpdateAndExpandInvalidation(&invalidation,
+ base_tiling_size,
+ CornerSinglePixelRect(corner, base_tiling_size));
// We should have lost nothing.
EXPECT_EQ(6, pile_->tiling().num_tiles_x());
UpdateWholePile();
UpdateAndExpandInvalidation(
- &invalidation, grow_right_tiling_size, gfx::Rect(1, 1));
+ &invalidation,
+ grow_right_tiling_size,
+ CornerSinglePixelRect(corner, grow_right_tiling_size));
// We should have lost the recordings in the right column.
EXPECT_EQ(6, pile_->tiling().num_tiles_x());
TestPicturePile::PictureMapKey key(i, j);
TestPicturePile::PictureMap& map = pile_->picture_map();
TestPicturePile::PictureMap::iterator it = map.find(key);
- EXPECT_EQ(i < 5, it != map.end() && it->second.GetPicture());
+ bool expect_tile;
+ switch (corner) {
+ case TOP_LEFT:
+ case BOTTOM_LEFT:
+ expect_tile = i < 5;
+ break;
+ case TOP_RIGHT:
+ // The interest rect in the top right tile means we'll record it.
+ expect_tile = i < 5 || (j == 0 && i == 5);
+ break;
+ case BOTTOM_RIGHT:
+ // The interest rect in the bottom right tile means we'll record it.
+ expect_tile = i < 5 || (j == 5 && i == 5);
+ break;
+ }
+ EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture());
}
}
- // We invalidated the right column.
- expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
- pile_->tiling().TileBounds(5, 5));
+ // We invalidated the right column outside the new interest rect. The tile
+ // that insects the interest rect in invalidated only on its new pixels.
+ switch (corner) {
+ case TOP_LEFT:
+ case BOTTOM_LEFT:
+ expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
+ pile_->tiling().TileBounds(5, 5));
+ break;
+ case TOP_RIGHT:
+ expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 1),
+ pile_->tiling().TileBounds(5, 5));
+ expected_invalidation.Union(SubtractRects(
+ pile_->tiling().TileBounds(5, 0), gfx::Rect(base_tiling_size)));
+ break;
+ case BOTTOM_RIGHT:
+ expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
+ pile_->tiling().TileBounds(5, 4));
+ expected_invalidation.Union(SubtractRects(
+ pile_->tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size)));
+ break;
+ }
EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
invalidation.Clear();
UpdateWholePile();
- UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1));
+ UpdateAndExpandInvalidation(&invalidation,
+ base_tiling_size,
+ CornerSinglePixelRect(corner, base_tiling_size));
// We should have lost nothing.
EXPECT_EQ(6, pile_->tiling().num_tiles_x());
UpdateWholePile();
UpdateAndExpandInvalidation(
- &invalidation, grow_both_tiling_size, gfx::Rect(1, 1));
+ &invalidation,
+ grow_both_tiling_size,
+ CornerSinglePixelRect(corner, grow_both_tiling_size));
- // We should have lost the recordings in the right column and bottom row.
+ // We should have lost the recordings in the right column and bottom row. The
+ // tile that insects the interest rect in invalidated only on its new pixels.
EXPECT_EQ(6, pile_->tiling().num_tiles_x());
EXPECT_EQ(6, pile_->tiling().num_tiles_y());
for (int i = 0; i < pile_->tiling().num_tiles_x(); ++i) {
TestPicturePile::PictureMapKey key(i, j);
TestPicturePile::PictureMap& map = pile_->picture_map();
TestPicturePile::PictureMap::iterator it = map.find(key);
- EXPECT_EQ(i < 5 && j < 5, it != map.end() && it->second.GetPicture());
+ bool expect_tile;
+ switch (corner) {
+ case TOP_LEFT:
+ expect_tile = i < 5 && j < 5;
+ break;
+ case TOP_RIGHT:
+ // The interest rect in the top right tile means we'll record it.
+ expect_tile = (i < 5 && j < 5) || (j == 0 && i == 5);
+ break;
+ case BOTTOM_LEFT:
+ // The interest rect in the bottom left tile means we'll record it.
+ expect_tile = (i < 5 && j < 5) || (j == 5 && i == 0);
+ break;
+ case BOTTOM_RIGHT:
+ // The interest rect in the bottom right tile means we'll record it.
+ expect_tile = (i < 5 && j < 5) || (j == 5 && i == 5);
+ break;
+ }
+ EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture())
+ << i << "," << j;
}
}
- // We invalidated the right column and the bottom row.
- expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
- pile_->tiling().TileBounds(5, 5));
- expected_invalidation.Union(gfx::UnionRects(
- pile_->tiling().TileBounds(0, 5), pile_->tiling().TileBounds(5, 5)));
+ // We invalidated the right column and the bottom row outside the new interest
+ // rect. The tile that insects the interest rect in invalidated only on its
+ // new pixels.
+ switch (corner) {
+ case TOP_LEFT:
+ expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
+ pile_->tiling().TileBounds(5, 5));
+ expected_invalidation.Union(gfx::UnionRects(
+ pile_->tiling().TileBounds(0, 5), pile_->tiling().TileBounds(5, 5)));
+ break;
+ case TOP_RIGHT:
+ expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 1),
+ pile_->tiling().TileBounds(5, 5));
+ expected_invalidation.Union(gfx::UnionRects(
+ pile_->tiling().TileBounds(0, 5), pile_->tiling().TileBounds(5, 5)));
+ expected_invalidation.Union(SubtractRects(
+ pile_->tiling().TileBounds(5, 0), gfx::Rect(base_tiling_size)));
+ break;
+ case BOTTOM_LEFT:
+ expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
+ pile_->tiling().TileBounds(5, 5));
+ expected_invalidation.Union(gfx::UnionRects(
+ pile_->tiling().TileBounds(1, 5), pile_->tiling().TileBounds(5, 5)));
+ expected_invalidation.Union(SubtractRects(
+ pile_->tiling().TileBounds(0, 5), gfx::Rect(base_tiling_size)));
+ break;
+ case BOTTOM_RIGHT:
+ expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
+ pile_->tiling().TileBounds(5, 4));
+ expected_invalidation.Union(gfx::UnionRects(
+ pile_->tiling().TileBounds(0, 5), pile_->tiling().TileBounds(4, 5)));
+ expected_invalidation.Union(SubtractRegions(
+ pile_->tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size)));
+ break;
+ }
EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
invalidation.Clear();
UpdateWholePile();
- UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1));
+ UpdateAndExpandInvalidation(&invalidation,
+ base_tiling_size,
+ CornerSinglePixelRect(corner, base_tiling_size));
// We should have lost nothing.
EXPECT_EQ(6, pile_->tiling().num_tiles_x());
invalidation.Clear();
}
+INSTANTIATE_TEST_CASE_P(
+ PicturePileResizeCornerTests,
+ PicturePileResizeCornerTest,
+ ::testing::Values(TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT));
+
TEST_F(PicturePileTest, ResizePileInsideInterestRect) {
// This size chosen to be small enough that all the rects below fit inside the
// the interest rect, so they are smaller than kPixelDistanceToRecord in each
}
// We invalidated the newly exposed pixels on the bottom row of tiles.
- expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(0, 5),
- pile_->tiling().TileBounds(5, 5));
- expected_invalidation.Subtract(gfx::Rect(base_tiling_size));
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size),
+ gfx::Rect(base_tiling_size));
+ Region bottom_row_new_pixels =
+ SubtractRegions(gfx::UnionRects(pile_->tiling().TileBounds(0, 5),
+ pile_->tiling().TileBounds(5, 5)),
+ gfx::Rect(base_tiling_size));
+ EXPECT_TRUE(expected_invalidation.Contains(bottom_row_new_pixels));
EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
invalidation.Clear();
}
// We invalidated the newly exposed pixels on the right column of tiles.
- expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
- pile_->tiling().TileBounds(5, 5));
- expected_invalidation.Subtract(gfx::Rect(base_tiling_size));
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size),
+ gfx::Rect(base_tiling_size));
+ Region right_column_new_pixels =
+ SubtractRegions(gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
+ pile_->tiling().TileBounds(5, 5)),
+ gfx::Rect(base_tiling_size));
+ EXPECT_TRUE(expected_invalidation.Contains(right_column_new_pixels));
EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
invalidation.Clear();
// We invalidated the newly exposed pixels on the bottom row and right column
// of tiles.
- expected_invalidation =
- UnionRegions(gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size),
+ gfx::Rect(base_tiling_size));
+ Region bottom_row_and_right_column_new_pixels = SubtractRegions(
+ UnionRegions(gfx::UnionRects(pile_->tiling().TileBounds(0, 5),
pile_->tiling().TileBounds(5, 5)),
- gfx::UnionRects(pile_->tiling().TileBounds(0, 5),
- pile_->tiling().TileBounds(5, 5)));
- expected_invalidation.Subtract(gfx::Rect(base_tiling_size));
+ gfx::UnionRects(pile_->tiling().TileBounds(5, 0),
+ pile_->tiling().TileBounds(5, 5))),
+ gfx::Rect(base_tiling_size));
+ EXPECT_TRUE(
+ expected_invalidation.Contains(bottom_row_and_right_column_new_pixels));
EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
invalidation.Clear();
continue;
}
- // All raster tasks need to be throttled by bytes of pending uploads.
+ // All raster tasks need to be throttled by bytes of pending uploads,
+ // but if it's the only task allow it to complete no matter what its size,
+ // to prevent starvation of the task queue.
size_t new_bytes_pending_upload = bytes_pending_upload;
new_bytes_pending_upload += task->resource()->bytes();
- if (new_bytes_pending_upload > max_bytes_pending_upload_) {
+ if (new_bytes_pending_upload > max_bytes_pending_upload_ &&
+ bytes_pending_upload) {
did_throttle_raster_tasks = true;
if (item.required_for_activation)
did_throttle_raster_tasks_required_for_activation = true;
namespace {
+bool TilePriorityTieBreaker(const Tile* tile_i, const Tile* tile_j) {
+ // When two tiles has same priority use Id as tie breaker.
+ return tile_i->id() < tile_j->id();
+}
+
typedef std::vector<Tile*> TileVector;
void SortBinTiles(ManagedTileBin bin, TileVector* tiles) {
switch (bin) {
- case NOW_AND_READY_TO_DRAW_BIN:
case NEVER_BIN:
break;
+ case NOW_AND_READY_TO_DRAW_BIN:
+ std::sort(tiles->begin(), tiles->end(), TilePriorityTieBreaker);
+ break;
case NOW_BIN:
case SOON_BIN:
case EVENTUALLY_AND_ACTIVE_BIN:
namespace cc {
namespace {
+const size_t kMaxTransferBufferUsageBytes = 10000U;
+// A resource of this dimension^2 * 4 must be greater than the above transfer
+// buffer constant.
+const size_t kLargeResourceDimension = 1000U;
+
enum RasterWorkerPoolType {
RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER,
RASTER_WORKER_POOL_TYPE_IMAGE,
RasterWorkerPool::GetTaskGraphRunner(),
context_provider_.get(),
resource_provider_.get(),
- std::numeric_limits<size_t>::max());
+ kMaxTransferBufferUsageBytes);
break;
case RASTER_WORKER_POOL_TYPE_IMAGE:
raster_worker_pool_ = ImageRasterWorkerPool::Create(
raster_worker_pool_->AsRasterizer()->ScheduleTasks(&queue);
}
- void AppendTask(unsigned id) {
- const gfx::Size size(1, 1);
-
+ void AppendTask(unsigned id, const gfx::Size& size) {
scoped_ptr<ScopedResource> resource(
ScopedResource::Create(resource_provider_.get()));
resource->Allocate(size, ResourceProvider::TextureUsageAny, RGBA_8888);
&empty));
}
+ void AppendTask(unsigned id) { AppendTask(id, gfx::Size(1, 1)); }
+
void AppendBlockingTask(unsigned id, base::Lock* lock) {
const gfx::Size size(1, 1);
RunMessageLoopUntilAllTasksHaveCompleted();
}
+TEST_P(RasterWorkerPoolTest, LargeResources) {
+ gfx::Size size(kLargeResourceDimension, kLargeResourceDimension);
+
+ {
+ // Verify a resource of this size is larger than the transfer buffer.
+ scoped_ptr<ScopedResource> resource(
+ ScopedResource::Create(resource_provider_.get()));
+ resource->Allocate(size, ResourceProvider::TextureUsageAny, RGBA_8888);
+ EXPECT_GE(resource->bytes(), kMaxTransferBufferUsageBytes);
+ }
+
+ AppendTask(0u, size);
+ AppendTask(1u, size);
+ AppendTask(2u, size);
+ ScheduleTasks();
+
+ // This will time out if a resource that is larger than the throttle limit
+ // never gets scheduled.
+ RunMessageLoopUntilAllTasksHaveCompleted();
+}
+
INSTANTIATE_TEST_CASE_P(RasterWorkerPoolTests,
RasterWorkerPoolTest,
::testing::Values(RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER,
return kSkia8888_GrPixelConfig;
}
-class IdentityAllocator : public SkBitmap::Allocator {
- public:
- explicit IdentityAllocator(void* buffer) : buffer_(buffer) {}
- virtual bool allocPixelRef(SkBitmap* dst, SkColorTable*) OVERRIDE {
- dst->setPixels(buffer_);
- return true;
- }
-
- private:
- void* buffer_;
-};
-
-void CopyBitmap(const SkBitmap& src, uint8_t* dst, SkColorType dst_colorType) {
- SkBitmap dst_bitmap;
- IdentityAllocator allocator(dst);
- src.copyTo(&dst_bitmap, dst_colorType, &allocator);
+void CopyBitmap(const SkBitmap& src, uint8_t* dst, SkColorType dst_color_type) {
+ SkImageInfo dst_info = src.info();
+ dst_info.fColorType = dst_color_type;
// TODO(kaanb): The GL pipeline assumes a 4-byte alignment for the
- // bitmap data. This check will be removed once crbug.com/293728 is fixed.
- CHECK_EQ(0u, dst_bitmap.rowBytes() % 4);
+ // bitmap data. There will be no need to call SkAlign4 once crbug.com/293728
+ // is fixed.
+ const size_t dst_row_bytes = SkAlign4(dst_info.minRowBytes());
+ CHECK_EQ(0u, dst_row_bytes % 4);
+ bool success = src.readPixels(dst_info, dst, dst_row_bytes, 0, 0);
+ CHECK_EQ(true, success);
}
class ScopedSetActiveTexture {
void set_picture_pile(scoped_refptr<PicturePileImpl> pile) {
DCHECK(pile->CanRaster(contents_scale_, content_rect_))
+ << "Recording rect: "
<< gfx::ScaleToEnclosingRect(content_rect_, 1.f / contents_scale_)
.ToString();
picture_pile_ = pile;
ManagedTileState::TileVersion& tile_version =
mts.tile_versions[HIGH_QUALITY_RASTER_MODE];
- tile_version.resource_ = resource_pool_->AcquireResource(gfx::Size(1, 1));
+ tile_version.resource_ =
+ resource_pool_->AcquireResource(tiles[i]->size());
bytes_releasable_ += BytesConsumedIfAllocated(tiles[i]);
++resources_releasable_;
{
ResourceProvider::ScopedWriteLockSoftware lock(
resource_provider_, plane_resources[0].resource_id);
- video_renderer_->Paint(video_frame.get(),
- lock.sk_canvas(),
- video_frame->visible_rect(),
- 0xff,
- media::VIDEO_ROTATION_0);
+ video_renderer_->Copy(video_frame.get(), lock.sk_canvas());
}
RecycleResourceData recycle_data = {
using PictureLayerImpl::MarkVisibleResourcesAsRequired;
using PictureLayerImpl::DoPostCommitInitializationIfNeeded;
using PictureLayerImpl::MinimumContentsScale;
+ using PictureLayerImpl::GetViewportForTilePriorityInContentSpace;
using PictureLayerImpl::SanityCheckTilingState;
+ using PictureLayerImpl::GetRecycledTwinLayer;
using PictureLayerImpl::UpdateIdealScales;
using PictureLayerImpl::MaximumTilingContentsScale;
: tile_manager_(new FakeTileManager(&tile_manager_client_)),
pile_(FakePicturePileImpl::CreateInfiniteFilledPile()),
twin_tiling_(NULL),
+ recycled_twin_tiling_(NULL),
allow_create_tile_(true),
max_tiles_for_interest_area_(10000),
skewport_target_time_in_seconds_(1.0f),
- skewport_extrapolation_limit_in_content_pixels_(2000) {}
+ skewport_extrapolation_limit_in_content_pixels_(2000) {
+}
FakePictureLayerTilingClient::FakePictureLayerTilingClient(
ResourceProvider* resource_provider)
new FakeTileManager(&tile_manager_client_, resource_pool_.get())),
pile_(FakePicturePileImpl::CreateInfiniteFilledPile()),
twin_tiling_(NULL),
+ recycled_twin_tiling_(NULL),
allow_create_tile_(true),
max_tiles_for_interest_area_(10000),
- skewport_target_time_in_seconds_(1.0f) {}
+ skewport_target_time_in_seconds_(1.0f) {
+}
FakePictureLayerTilingClient::~FakePictureLayerTilingClient() {
}
return twin_tiling_;
}
+PictureLayerTiling* FakePictureLayerTilingClient::GetRecycledTwinTiling(
+ const PictureLayerTiling* tiling) {
+ return recycled_twin_tiling_;
+}
+
WhichTree FakePictureLayerTilingClient::GetTree() const {
return tree_;
}
virtual const Region* GetInvalidation() OVERRIDE;
virtual const PictureLayerTiling* GetTwinTiling(
const PictureLayerTiling* tiling) const OVERRIDE;
+ virtual PictureLayerTiling* GetRecycledTwinTiling(
+ const PictureLayerTiling* tiling) OVERRIDE;
virtual WhichTree GetTree() const OVERRIDE;
void set_twin_tiling(PictureLayerTiling* tiling) { twin_tiling_ = tiling; }
+ void set_recycled_twin_tiling(PictureLayerTiling* tiling) {
+ recycled_twin_tiling_ = tiling;
+ }
void set_text_rect(const gfx::Rect& rect) { text_rect_ = rect; }
void set_allow_create_tile(bool allow) { allow_create_tile_ = allow; }
void set_invalidation(const Region& region) { invalidation_ = region; }
scoped_refptr<PicturePileImpl> pile_;
gfx::Size tile_size_;
PictureLayerTiling* twin_tiling_;
+ PictureLayerTiling* recycled_twin_tiling_;
gfx::Rect text_rect_;
bool allow_create_tile_;
Region invalidation_;
attribs.bind_generates_resource = false;
gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
- scoped_ptr<gpu::GLInProcessContext> context = make_scoped_ptr(
- gpu::GLInProcessContext::Create(NULL,
- NULL,
- is_offscreen,
- gfx::kNullAcceleratedWidget,
- gfx::Size(1, 1),
- NULL,
- share_resources,
- attribs,
- gpu_preference));
+ scoped_ptr<gpu::GLInProcessContext> context =
+ make_scoped_ptr(gpu::GLInProcessContext::Create(
+ NULL,
+ NULL,
+ is_offscreen,
+ gfx::kNullAcceleratedWidget,
+ gfx::Size(1, 1),
+ NULL,
+ share_resources,
+ attribs,
+ gpu_preference,
+ gpu::GLInProcessContextSharedMemoryLimits()));
DCHECK(context);
return context.Pass();
recycle_tree_.reset();
}
+void LayerTreeHostImpl::ResetRecycleTreeForTesting() {
+ if (recycle_tree_)
+ recycle_tree_->DetachLayerTree();
+ recycle_tree_.reset();
+}
+
void LayerTreeHostImpl::EnforceManagedMemoryPolicy(
const ManagedMemoryPolicy& policy) {
// Resets all of the trees to an empty state.
void ResetTreesForTesting();
+ void ResetRecycleTreeForTesting();
DrawMode GetDrawMode() const;
wheel_scroll_delta);
}
+TEST_F(LayerTreeHostImplTest, ScrollViewportRounding) {
+ int width = 332;
+ int height = 20;
+ int scale = 3;
+ SetupScrollAndContentsLayers(gfx::Size(width, height));
+ host_impl_->SetViewportSize(gfx::Size(width * scale - 1, height * scale));
+ host_impl_->SetDeviceScaleFactor(scale);
+ host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f, 0.5f, 4.f);
+
+ LayerImpl* inner_viewport_scroll_layer =
+ host_impl_->active_tree()->InnerViewportScrollLayer();
+ EXPECT_EQ(gfx::Vector2d(0, 0),
+ inner_viewport_scroll_layer->MaxScrollOffset());
+}
+
class TestScrollOffsetDelegate : public LayerScrollOffsetDelegate {
public:
TestScrollOffsetDelegate()
device_scale_factor(),
total_page_scale_factor(),
page_scale_layer,
- MaxTextureSize(),
+ resource_provider()->max_texture_size(),
settings().can_use_lcd_text,
can_render_to_separate_surface,
settings().layer_transforms_should_scale_layer_contents,
return tree->LayerById(id);
}
-int LayerTreeImpl::MaxTextureSize() const {
- return layer_tree_host_impl_->GetRendererCapabilities().max_texture_size;
-}
-
bool LayerTreeImpl::PinchGestureActive() const {
return layer_tree_host_impl_->pinch_gesture_active();
}
LayerImpl* FindActiveTreeLayerById(int id);
LayerImpl* FindPendingTreeLayerById(int id);
LayerImpl* FindRecycleTreeLayerById(int id);
- int MaxTextureSize() const;
bool PinchGestureActive() const;
base::TimeTicks CurrentFrameTimeTicks() const;
base::TimeDelta begin_impl_frame_interval() const;
impl().scheduler.reset();
impl().layer_tree_host_impl.reset();
impl().weak_factory.InvalidateWeakPtrs();
+ // We need to explicitly cancel the notifier, since it isn't using weak ptrs.
+ // TODO(vmpstr): We should see if we can make it use weak ptrs and still keep
+ // the convention of having a weak ptr factory initialized last. Alternatively
+ // we should moved the notifier (and RenewTreePriority) to LTHI. See
+ // crbug.com/411972
+ impl().smoothness_priority_expiration_notifier.Cancel();
impl().contents_texture_manager = NULL;
completion->Signal();
}
MAJOR=38
MINOR=0
BUILD=2125
-PATCH=23
+PATCH=66
<alpha android:interpolator="@android:anim/linear_interpolator"
android:fromAlpha="0" android:toAlpha="1"
android:duration="200" />
+ <translate android:interpolator="@interpolator/transform_curve_interpolator"
+ android:fromYDelta="@dimen/menu_negative_software_vertical_offset"
+ android:toYDelta="0"
+ android:duration="200" />
</set>
\ No newline at end of file
<resources>
<!-- Menu Dimensions -->
<!-- Necessary to align the menu icon with the actual button. -->
- <dimen name="menu_software_vertical_offset">-6dp</dimen>
+ <dimen name="menu_negative_software_vertical_offset">6dp</dimen>
</resources>
\ No newline at end of file
<!-- Custom Menu dimensions -->
<dimen name="menu_width">258dp</dimen>
- <dimen name="menu_software_vertical_offset">0dp</dimen>
+ <dimen name="menu_negative_software_vertical_offset">0dp</dimen>
<!-- The amount to fade the edges of the menu to indicate more content is available
via scrolling. -->
<dimen name="menu_vertical_fade_distance">15dp</dimen>
public void onContextMenuShown(Tab tab, ContextMenu menu) { }
@Override
+ public void onWebContentsInstantSupportDisabled() { }
+
+ @Override
public void onLoadStarted(Tab tabBase) { }
@Override
--- /dev/null
+// Copyright 2014 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.chrome.browser;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.v4.content.FileProvider;
+
+import org.chromium.base.ContentUriUtils;
+
+import java.io.File;
+
+/**
+ * Utilities for translating a file into content URI.
+ */
+public class FileProviderHelper implements ContentUriUtils.FileProviderUtil {
+ // Keep this variable in sync with the value defined in file_paths.xml.
+ private static final String API_AUTHORITY_SUFFIX = ".FileProvider";
+
+ @Override
+ public Uri getContentUriFromFile(Context context, File file) {
+ return FileProvider.getUriForFile(context,
+ context.getPackageName() + API_AUTHORITY_SUFFIX, file);
+ }
+}
}
for (TabObserver observer : mObservers) observer.onContentChanged(this);
+
+ // For browser tabs, we want to set accessibility focus to the page
+ // when it loads. This is not the default behavior for embedded
+ // web views.
+ mContentViewCore.setShouldSetAccessibilityFocusOnPageLoad(true);
}
/**
}
/**
+ * A helper method to allow subclasses to handle the Instant support
+ * disabled event.
+ */
+ @CalledByNative
+ private void onWebContentsInstantSupportDisabled() {
+ for (TabObserver observer : mObservers) observer.onWebContentsInstantSupportDisabled();
+ }
+
+ /**
* A helper method to allow subclasses to build their own menu populator.
* @return An instance of a {@link ContextMenuPopulator}.
*/
*/
void onContextMenuShown(Tab tab, ContextMenu menu);
+ /**
+ * Called when the WebContents Instant support is disabled.
+ */
+ void onWebContentsInstantSupportDisabled();
+
// WebContentsDelegateAndroid methods ---------------------------------------------------------
/**
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
private final int mItemRowHeight;
private final int mItemDividerHeight;
private final int mVerticalFadeDistance;
- private final int mAdditionalVerticalOffset;
+ private final int mNegativeSoftwareVerticalOffset;
private ListPopupWindow mPopup;
private AppMenuAdapter mAdapter;
private AppMenuHandler mHandler;
mItemDividerHeight = itemDividerHeight;
assert mItemDividerHeight >= 0;
- mAdditionalVerticalOffset =
- res.getDimensionPixelSize(R.dimen.menu_software_vertical_offset);
+ mNegativeSoftwareVerticalOffset =
+ res.getDimensionPixelSize(R.dimen.menu_negative_software_vertical_offset);
mVerticalFadeDistance = res.getDimensionPixelSize(R.dimen.menu_vertical_fade_distance);
}
}
});
+ // Some OEMs don't actually let us change the background... but they still return the
+ // padding of the new background, which breaks the menu height. If we still have a
+ // drawable here even though our style says @null we should use this padding instead...
+ Drawable originalBgDrawable = mPopup.getBackground();
+
// Need to explicitly set the background here. Relying on it being set in the style caused
// an incorrectly drawn background.
if (isByHardwareButton) {
}
}
+ Rect sizingPadding = new Rect(bgPadding);
+ if (isByHardwareButton && originalBgDrawable != null) {
+ Rect originalPadding = new Rect();
+ originalBgDrawable.getPadding(originalPadding);
+ sizingPadding.top = originalPadding.top;
+ sizingPadding.bottom = originalPadding.bottom;
+ }
+
boolean showMenuButton = !mIsByHardwareButton;
if (!SHOW_SW_MENU_BUTTON) showMenuButton = false;
// A List adapter for visible items in the Menu. The first row is added as a header to the
this, menuItems, LayoutInflater.from(context), showMenuButton);
mPopup.setAdapter(mAdapter);
- setMenuHeight(menuItems.size(), visibleDisplayFrame, screenHeight);
- setPopupOffset(mPopup, mCurrentScreenRotation, visibleDisplayFrame);
+ setMenuHeight(menuItems.size(), visibleDisplayFrame, screenHeight, sizingPadding);
+ setPopupOffset(mPopup, mCurrentScreenRotation, visibleDisplayFrame, sizingPadding);
mPopup.setOnItemClickListener(this);
mPopup.show();
mPopup.getListView().setItemsCanFocus(true);
}
}
- private void setPopupOffset(ListPopupWindow popup, int screenRotation, Rect appRect) {
- Rect paddingRect = new Rect();
- popup.getBackground().getPadding(paddingRect);
+ private void setPopupOffset(
+ ListPopupWindow popup, int screenRotation, Rect appRect, Rect padding) {
int[] anchorLocation = new int[2];
popup.getAnchorView().getLocationInWindow(anchorLocation);
int anchorHeight = popup.getAnchorView().getHeight();
break;
}
popup.setHorizontalOffset(horizontalOffset);
- // The menu is displayed above the anchored view, so shift the menu up by the top
+ // The menu is displayed above the anchored view, so shift the menu up by the bottom
// padding of the background.
- popup.setVerticalOffset(-paddingRect.bottom);
+ popup.setVerticalOffset(-padding.bottom);
} else {
// The menu is displayed over and below the anchored view, so shift the menu up by the
// height of the anchor view.
- popup.setVerticalOffset(mAdditionalVerticalOffset - anchorHeight);
+ popup.setVerticalOffset(-mNegativeSoftwareVerticalOffset - anchorHeight);
}
}
return mPopup;
}
- private void setMenuHeight(int numMenuItems, Rect appDimensions, int screenHeight) {
+ private void setMenuHeight(
+ int numMenuItems, Rect appDimensions, int screenHeight, Rect padding) {
assert mPopup.getAnchorView() != null;
View anchorView = mPopup.getAnchorView();
int[] anchorViewLocation = new int[2];
int availableScreenSpace = Math.max(anchorViewLocation[1],
appDimensions.height() - anchorViewLocation[1] - anchorViewImpactHeight);
- Rect padding = new Rect();
- mPopup.getBackground().getPadding(padding);
- availableScreenSpace -= mIsByHardwareButton ? padding.top : padding.bottom;
+ availableScreenSpace -= padding.bottom;
+ if (mIsByHardwareButton) availableScreenSpace -= padding.top;
int numCanFit = availableScreenSpace / (mItemRowHeight + mItemDividerHeight);
return (Profile) nativeGetLastUsedProfile();
}
+ public Profile getOriginalProfile() {
+ return (Profile) nativeGetOriginalProfile(mNativeProfileAndroid);
+ }
+
+ public Profile getOffTheRecordProfile() {
+ return (Profile) nativeGetOffTheRecordProfile(mNativeProfileAndroid);
+ }
+
+ public boolean hasOffTheRecordProfile() {
+ return nativeHasOffTheRecordProfile(mNativeProfileAndroid);
+ }
+
+ public boolean isOffTheRecord() {
+ return nativeIsOffTheRecord(mNativeProfileAndroid);
+ }
+
@CalledByNative
private static Profile create(long nativeProfileAndroid) {
return new Profile(nativeProfileAndroid);
}
private static native Object nativeGetLastUsedProfile();
+ private native Object nativeGetOriginalProfile(
+ long nativeProfileAndroid);
+ private native Object nativeGetOffTheRecordProfile(
+ long nativeProfileAndroid);
+ private native boolean nativeHasOffTheRecordProfile(
+ long nativeProfileAndroid);
+ private native boolean nativeIsOffTheRecord(
+ long nativeProfileAndroid);
}
* @param screenshot Screenshot of the page to be shared.
*/
public static void share(boolean shareDirectly, Activity activity, String title, String url,
- Bitmap screenshot) {
+ Bitmap screenshot, int extraIntentFlags) {
if (shareDirectly) {
- shareWithLastUsed(activity, title, url, screenshot);
+ shareWithLastUsed(activity, title, url, screenshot, extraIntentFlags);
} else {
- showShareDialog(activity, title, url, screenshot);
+ showShareDialog(activity, title, url, screenshot, extraIntentFlags);
}
}
* @param title Title of the page to be shared.
* @param url URL of the page to be shared.
* @param screenshot Screenshot of the page to be shared.
+ * @param extraIntentFlags Additional flags that should be added to the share intent.
*/
private static void showShareDialog(final Activity activity, final String title,
- final String url, final Bitmap screenshot) {
- Intent intent = getShareIntent(title, url, screenshot);
+ final String url, final Bitmap screenshot, final int extraIntentFlags) {
+ Intent intent = getShareIntent(title, url, screenshot, extraIntentFlags);
PackageManager manager = activity.getPackageManager();
List<ResolveInfo> resolveInfoList = manager.queryIntentActivities(intent, 0);
assert resolveInfoList.size() > 0;
ComponentName component =
new ComponentName(ai.applicationInfo.packageName, ai.name);
setLastShareComponentName(activity, component);
- Intent intent = getDirectShareIntentForComponent(title, url, screenshot, component);
+ Intent intent = getDirectShareIntentForComponent(title, url, screenshot, component,
+ extraIntentFlags);
activity.startActivity(intent);
dialog.dismiss();
}
* @param title Title of the page to be shared.
* @param url URL of the page to be shared.
* @param screenshot Screenshot of the page to be shared.
+ * @param extraIntentFlags Additional flags that should be added to the share intent.
*/
private static void shareWithLastUsed(
- Activity activity, String title, String url, Bitmap screenshot) {
+ Activity activity, String title, String url, Bitmap screenshot, int extraIntentFlags) {
ComponentName component = getLastShareComponentName(activity);
if (component == null) return;
- Intent intent = getDirectShareIntentForComponent(title, url, screenshot, component);
+ Intent intent = getDirectShareIntentForComponent(
+ title, url, screenshot, component, extraIntentFlags);
activity.startActivity(intent);
}
}
}
- private static Intent getShareIntent(String title, String url, Bitmap screenshot) {
+ private static Intent getShareIntent(String title, String url, Bitmap screenshot,
+ int extraIntentFlags) {
Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.addFlags(extraIntentFlags);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_SUBJECT, title);
intent.putExtra(Intent.EXTRA_TEXT, url);
}
private static Intent getDirectShareIntentForComponent(String title, String url,
- Bitmap screenshot, ComponentName component) {
- Intent intent = getShareIntent(title, url, screenshot);
+ Bitmap screenshot, ComponentName component, int extraIntentFlags) {
+ Intent intent = getShareIntent(title, url, screenshot, extraIntentFlags);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
| Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
intent.setComponent(component);
ChromeSigninController.get(mContext).getSignedInUser() == null;
}
+ /**
+ * Returns true if signin is disabled by policy.
+ */
+ public boolean isSigninDisabledByPolicy() {
+ return !mSigninAllowedByPolicy;
+ }
+
public void addSignInAllowedObserver(SignInAllowedObserver observer) {
mSignInAllowedObservers.addObserver(observer);
}
import org.chromium.chrome.browser.Tab;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.util.MathUtils;
+import org.chromium.content_public.browser.WebContents;
import java.util.ArrayList;
import java.util.List;
// TODO(dtrainor): Update the list of undoable tabs instead of committing it.
if (!canUndo) commitAllTabClosures();
+
+ // Cancel any media currently playing.
+ if (canUndo) {
+ WebContents webContents = tab.getWebContents();
+ if (webContents != null) webContents.releaseMediaPlayers();
+ }
+
mTabs.remove(tab);
boolean nextIsIncognito = nextTab == null ? false : nextTab.isIncognito();
android:authorities="org.chromium.chrome.shell"
android:exported="true" />
+ <!-- Provider for FileProvider. -->
+ <provider android:name="android.support.v4.content.FileProvider"
+ android:authorities="org.chromium.chrome.shell.FileProvider"
+ android:exported="false"
+ android:grantUriPermissions="true">
+ <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/file_paths" />
+ </provider>
+
<!-- Sync adapter for browser sync. -->
<service android:exported="false"
android:name="org.chromium.chrome.shell.sync.ChromeShellSyncAdapterService">
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.BaseSwitches;
import org.chromium.base.CommandLine;
+import org.chromium.base.ContentUriUtils;
import org.chromium.base.MemoryPressureListener;
import org.chromium.base.library_loader.ProcessInitException;
import org.chromium.chrome.browser.DevToolsServer;
+import org.chromium.chrome.browser.FileProviderHelper;
import org.chromium.chrome.browser.appmenu.AppMenuHandler;
import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate;
import org.chromium.chrome.browser.dom_distiller.DomDistillerTabUtils;
// In case this method is called after the first onStart(), we need to inform the
// SyncController that we have started.
mSyncController.onStart();
+ ContentUriUtils.setFileProviderUtil(new FileProviderHelper());
}
@Override
return super.onKeyDown(keyCode, event);
}
+ @SuppressWarnings("deprecation")
@Override
public boolean onOptionsItemSelected(MenuItem item) {
ChromeShellTab activeTab = getActiveTab();
case R.id.share_menu_id:
case R.id.direct_share_menu_id:
ShareHelper.share(item.getItemId() == R.id.direct_share_menu_id, this,
- activeTab.getTitle(), activeTab.getUrl(), null);
+ activeTab.getTitle(), activeTab.getUrl(), null,
+ Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
return true;
default:
return super.onOptionsItemSelected(item);
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- The attributes in this XML file provide configuration information -->
+<!-- for the ContentProvider. -->
+
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+ <files-path name="images" path="images/"/>
+</paths>
<string>BrowserCrApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
+ <key>NSUserActivityTypes</key>
+ <array>
+ <string>NSUserActivityTypeBrowsingWeb</string>
+ </array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<!-- Contextual Search -->
<message name="IDS_CONTEXTUAL_SEARCH_PROMO_DESCRIPTION" desc="Description of the Contextual Search feature.">
- Enabling "Tap to Search" provides an easy way to search for terms on a web page by simply tapping on them. The word tapped and surrounding page will be sent to Google.
+ “Touch to Search” provides an easy way to search for terms on a web page by simply touching on them. The touched word and surrounding page will be sent to Google.
</message>
<message name="IDS_CONTEXTUAL_SEARCH_PROMO_OPTIN" desc="Text for the button the user clicks to opt in.">
Yes, please
</message>
<!-- Strings for notification shown when the Chromebook is added to Easy Unlock -->
<message name="IDS_EASY_UNLOCK_CHROMEBOOK_ADDED_NOTIFICATION_TITLE" desc="Title for notification shown when this Chromebook is added to Easy Unlock as an additional Easy Unlock device.">
- Easy unock is now enabled
+ Easy unlock is now enabled
</message>
<message name="IDS_EASY_UNLOCK_CHROMEBOOK_ADDED_NOTIFICATION_MESSAGE" desc="Message for the notification shown when this Chromebook is added to Easy Unlock as an additional Easy Unlock device.">
You can start using your Android phone to unlock this <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>, too - no additional setup necessary.
<!-- Contextual Search -->
<message name="IDS_CONTEXTUAL_SEARCH_PROMO_DESCRIPTION" desc="Description of the Contextual Search feature.">
- Enabling "Tap to Search" provides an easy way to search for terms on a web page by simply tapping on them. The word tapped and surrounding page will be sent to Google.
+ “Touch to Search” provides an easy way to search for terms on a web page by simply touching on them. The touched word and surrounding page will be sent to Google.
</message>
<message name="IDS_CONTEXTUAL_SEARCH_PROMO_OPTIN" desc="Text for the button the user clicks to opt in.">
Yes, please
<!-- The default value for |WebPreference::standard_font_family_map| for
Japanese script. -->
<message name="IDS_STANDARD_FONT_FAMILY_JAPANESE" use_name_for_id="true">
- IPAPGothic
+ Noto Sans CJK Japanese
</message>
<!-- The default value for |WebPreference::fixed_font_family_map| for
<!-- The default value for |WebPreference::sans_serif_font_family_map| for
Japanese script. -->
<message name="IDS_SANS_SERIF_FONT_FAMILY_JAPANESE" use_name_for_id="true">
- IPAPGothic
+ Noto Sans CJK Japanese
</message>
<!-- The default value for |WebPreference::standard_font_family_map| for
Korean script. -->
<message name="IDS_STANDARD_FONT_FAMILY_KOREAN" use_name_for_id="true">
- NanumGothic
+ Noto Sans CJK Korean
</message>
<!-- The default value for |WebPreference::fixed_font_family_map| for
<!-- The default value for |WebPreference::sans_serif_font_family_map| for
Korean script. -->
<message name="IDS_SANS_SERIF_FONT_FAMILY_KOREAN" use_name_for_id="true">
- NanumGothic
+ Noto Sans CJK Korean
</message>
<!-- The default value for |WebPreference::standard_font_family_map| for
Simplified Chinese script. -->
<message name="IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN" use_name_for_id="true">
- Droid Sans Fallback
+ Noto Sans CJK Simplified Chinese
</message>
<!-- The default value for |WebPreference::fixed_font_family_map| for
Simplified Chinese script. -->
<message name="IDS_FIXED_FONT_FAMILY_SIMPLIFIED_HAN" use_name_for_id="true">
- Droid Sans Fallback
+ Noto Sans CJK Simplfieid Chinese
</message>
<!-- The default value for |WebPreference::serif_font_family_map| for
Simplified Chinese script. -->
<message name="IDS_SERIF_FONT_FAMILY_SIMPLIFIED_HAN" use_name_for_id="true">
- Droid Sans Fallback
+ Noto Sans CJK Simplfieid Chinese
</message>
<!-- The default value for |WebPreference::sans_serif_font_family_map| for
Simplified Chinese script. -->
<message name="IDS_SANS_SERIF_FONT_FAMILY_SIMPLIFIED_HAN" use_name_for_id="true">
- Droid Sans Fallback
+ Noto Sans CJK Simplfieid Chinese
</message>
<!-- The default value for |WebPreference::standard_font_family_map| for
Traditional Chinese script. -->
<message name="IDS_STANDARD_FONT_FAMILY_TRADITIONAL_HAN" use_name_for_id="true">
- Droid Sans Fallback
+ Noto Sans CJK Traditional Chinese
</message>
<!-- The default value for |WebPreference::fixed_font_family_map| for
Traditional Chinese script. -->
<message name="IDS_FIXED_FONT_FAMILY_TRADITIONAL_HAN" use_name_for_id="true">
- Droid Sans Fallback
+ Noto Sans CJK Traditional Chinese
</message>
<!-- The default value for |WebPreference::serif_font_family_map| for
Traditional Chinese script. -->
<message name="IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN" use_name_for_id="true">
- Droid Sans Fallback
+ Noto Sans CJK Traditional Chinese
</message>
<!-- The default value for |WebPreference::sans_serif_font_family_map| for
Traditional Chinese script. -->
<message name="IDS_SANS_SERIF_FONT_FAMILY_TRADITIONAL_HAN" use_name_for_id="true">
- Droid Sans Fallback
+ Noto Sans CJK Traditional Chinese
</message>
<!-- The default value for |WebPreference::default_font_size| -->
<!-- The default value for |WebPreference::standard_font_family_map| for
Japanese script. -->
<message name="IDS_STANDARD_FONT_FAMILY_JAPANESE" use_name_for_id="true">
- MotoyaG04Gothic
+ Noto Sans CJK Japanese
</message>
<!-- The default value for |WebPreference::fixed_font_family_map| for
<!-- The default value for |WebPreference::sans_serif_font_family_map| for
Japanese script. -->
<message name="IDS_SANS_SERIF_FONT_FAMILY_JAPANESE" use_name_for_id="true">
- MotoyaG04Gothic
+ Noto Sans CJK Japanese
</message>
<!-- The default value for |WebPreference::standard_font_family_map| for
Korean script. -->
<message name="IDS_STANDARD_FONT_FAMILY_KOREAN" use_name_for_id="true">
- NanumGothic
+ Noto Sans CJK Korean
</message>
<!-- The default value for |WebPreference::fixed_font_family_map| for
<!-- The default value for |WebPreference::sans_serif_font_family_map| for
Korean script. -->
<message name="IDS_SANS_SERIF_FONT_FAMILY_KOREAN" use_name_for_id="true">
- NanumGothic
+ Noto Sans CJK Korean
</message>
<!-- The default value for |WebPreference::standard_font_family_map| for
Simplified Chinese script. -->
<message name="IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN" use_name_for_id="true">
- MYingHeiGB18030
+ Noto Sans CJK Simplified Chinese
</message>
<!-- The default value for |WebPreference::fixed_font_family_map| for
Simplified Chinese script. -->
<message name="IDS_FIXED_FONT_FAMILY_SIMPLIFIED_HAN" use_name_for_id="true">
- MYingHeiGB18030
+ Noto Sans CJK Simplified Chinese
</message>
<!-- The default value for |WebPreference::serif_font_family_map| for
<!-- The default value for |WebPreference::sans_serif_font_family_map| for
Simplified Chinese script. -->
<message name="IDS_SANS_SERIF_FONT_FAMILY_SIMPLIFIED_HAN" use_name_for_id="true">
- MYingHeiGB18030
+ Noto Sans CJK Simplified Chinese
</message>
<!-- The default value for |WebPreference::standard_font_family_map| for
Traditional Chinese script. -->
<message name="IDS_STANDARD_FONT_FAMILY_TRADITIONAL_HAN" use_name_for_id="true">
- MYingHeiB5HK
+ Noto Sans CJK Traditional Chinese
</message>
<!-- The default value for |WebPreference::fixed_font_family_map| for
Traditional Chinese script. -->
<message name="IDS_FIXED_FONT_FAMILY_TRADITIONAL_HAN" use_name_for_id="true">
- MYingHeiB5HK
+ Noto Sans CJK Traditional Chinese
</message>
<!-- The default value for |WebPreference::serif_font_family_map| for
<!-- The default value for |WebPreference::sans_serif_font_family_map| for
Traditional Chinese script. -->
<message name="IDS_SANS_SERIF_FONT_FAMILY_TRADITIONAL_HAN" use_name_for_id="true">
- MYingHeiB5HK
+ Noto Sans CJK Traditional Chinese
</message>
<!-- The default value for |WebPreference::default_font_size| -->
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="ja">
+<translation id="IDS_STANDARD_FONT_FAMILY">Noto Sans CJK Japanese</translation>
+<translation id="IDS_SANS_SERIF_FONT_FAMILY">Noto Sans CJK Japanese</translation>
<if expr="_google_chrome">
- <translation id="IDS_STANDARD_FONT_FAMILY">MotoyaG04Gothic</translation>
<translation id="IDS_FIXED_FONT_FAMILY">MotoyaG04GothicMono</translation>
<translation id="IDS_SERIF_FONT_FAMILY">MotoyaG04Mincho</translation>
- <translation id="IDS_SANS_SERIF_FONT_FAMILY">MotoyaG04Gothic</translation>
</if>
<if expr="not _google_chrome">
- <translation id="IDS_STANDARD_FONT_FAMILY">IPAPGothic</translation>
<translation id="IDS_FIXED_FONT_FAMILY">IPAGothic</translation>
<translation id="IDS_SERIF_FONT_FAMILY">IPAPMincho</translation>
- <translation id="IDS_SANS_SERIF_FONT_FAMILY">IPAPGothic</translation>
</if>
<translation id="IDS_MINIMUM_FONT_SIZE">10</translation>
<translation id="IDS_MINIMUM_LOGICAL_FONT_SIZE">10</translation>
<!DOCTYPE translationbundle>
<translationbundle lang="ko">
<!-- TODO(jungshik): remove this line once we fix bug 14691 -->
-<translation id="IDS_STANDARD_FONT_FAMILY">NanumGothic</translation>
+<translation id="IDS_STANDARD_FONT_FAMILY">Noto Sans CJK Korean</translation>
<translation id="IDS_FIXED_FONT_FAMILY">Monospace</translation>
<translation id="IDS_SERIF_FONT_FAMILY">NanumMyeongjo</translation>
-<translation id="IDS_SANS_SERIF_FONT_FAMILY">NanumGothic</translation>
+<translation id="IDS_SANS_SERIF_FONT_FAMILY">Noto Sans CJK Korean</translation>
<translation id="IDS_MINIMUM_FONT_SIZE">10</translation>
<translation id="IDS_MINIMUM_LOGICAL_FONT_SIZE">10</translation>
</translationbundle>
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="zh-CN">
+<translation id="IDS_STANDARD_FONT_FAMILY">Noto Sans CJK Simplified Chinese</translation>
+<translation id="IDS_FIXED_FONT_FAMILY">Noto Sans CJK Simplified Chinese</translation>
+<translation id="IDS_SANS_SERIF_FONT_FAMILY">Noto Sans CJK Simplified Chinese</translation>
<if expr="_google_chrome">
- <translation id="IDS_STANDARD_FONT_FAMILY">MYingHeiGB18030</translation>
- <translation id="IDS_FIXED_FONT_FAMILY">MYingHeiGB18030</translation>
<translation id="IDS_SERIF_FONT_FAMILY">MSung GB18030</translation>
- <translation id="IDS_SANS_SERIF_FONT_FAMILY">MYingHeiGB18030</translation>
</if>
<if expr="not _google_chrome">
- <translation id="IDS_STANDARD_FONT_FAMILY">Droid Sans Fallback</translation>
- <translation id="IDS_FIXED_FONT_FAMILY">Droid Sans Fallback</translation>
- <translation id="IDS_SANS_SERIF_FONT_FAMILY">Droid Sans Fallback</translation>
+ <translation id="IDS_SERIF_FONT_FAMILY">Noto Sans CJK Simplified Chinese</translation>
</if>
<translation id="IDS_MINIMUM_FONT_SIZE">12</translation>
<translation id="IDS_MINIMUM_LOGICAL_FONT_SIZE">12</translation>
<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="zh-TW">
+<translation id="IDS_STANDARD_FONT_FAMILY">Noto Sans CJK Traditional Chinese</translation>
+<translation id="IDS_FIXED_FONT_FAMILY">Noto Sans CJK Traditional Chinese</translation>
+<translation id="IDS_SANS_SERIF_FONT_FAMILY">Noto Sans CJK Traditional Chinese</translation>
<if expr="_google_chrome">
- <translation id="IDS_STANDARD_FONT_FAMILY">MYingHeiB5HK</translation>
- <translation id="IDS_FIXED_FONT_FAMILY">MYingHeiB5HK</translation>
<translation id="IDS_SERIF_FONT_FAMILY">MSung B5HK</translation>
- <translation id="IDS_SANS_SERIF_FONT_FAMILY">MYingHeiB5HK</translation>
</if>
<if expr="not _google_chrome">
- <translation id="IDS_STANDARD_FONT_FAMILY">Droid Sans Fallback</translation>
- <translation id="IDS_FIXED_FONT_FAMILY">Droid Sans Fallback</translation>
- <translation id="IDS_SANS_SERIF_FONT_FAMILY">Droid Sans Fallback</translation>
+<translation id="IDS_SERIF_FONT_FAMILY">Noto Sans CJK Traditional Chinese</translation>
</if>
<translation id="IDS_MINIMUM_FONT_SIZE">12</translation>
<translation id="IDS_MINIMUM_LOGICAL_FONT_SIZE">12</translation>
"enable-password-generation",
IDS_FLAGS_ENABLE_PASSWORD_GENERATION_NAME,
IDS_FLAGS_ENABLE_PASSWORD_GENERATION_DESCRIPTION,
- kOsWin | kOsLinux | kOsCrOS,
+ kOsWin | kOsLinux | kOsCrOS | kOsMac,
ENABLE_DISABLE_VALUE_TYPE(autofill::switches::kEnablePasswordGeneration,
autofill::switches::kDisablePasswordGeneration)
},
if (!web_contents) {
// The tab has been pushed out of memory, pull it back.
TabAndroid* tab = model->GetTabAt(index);
+ if (!tab)
+ return NULL;
+
tab->LoadIfNeeded();
web_contents = model->GetWebContentsAt(index);
if (!web_contents)
TabModel* model = *iter;
for (int i = 0; i < model->GetTabCount(); ++i) {
TabAndroid* tab = model->GetTabAt(i);
- if (tab->GetAndroidId() == tab_id_) {
+ if (tab && tab->GetAndroidId() == tab_id_) {
*model_result = model;
*index_result = i;
return true;
TabModel* model = *iter;
for (int i = 0; i < model->GetTabCount(); ++i) {
TabAndroid* tab = model->GetTabAt(i);
+ if (!tab)
+ continue;
+
WebContents* web_contents = model->GetWebContentsAt(i);
if (web_contents) {
tab_web_contents.insert(web_contents);
#include "chrome/browser/profiles/profile_android.h"
#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
#include "chrome/browser/search/suggestions/suggestions_source.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/thumbnails/thumbnail_list_source.h"
#include "components/suggestions/suggestions_service.h"
+#include "components/suggestions/suggestions_utils.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/url_data_source.h"
using suggestions::SuggestionsProfile;
using suggestions::SuggestionsService;
using suggestions::SuggestionsServiceFactory;
+using suggestions::SyncState;
namespace {
counter->Add(position);
}
+// Return the current SyncState for use with the SuggestionsService.
+SyncState GetSyncState(Profile* profile) {
+ ProfileSyncService* sync =
+ ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
+ if (!sync)
+ return SyncState::SYNC_OR_HISTORY_SYNC_DISABLED;
+ return suggestions::GetSyncState(
+ sync->IsSyncEnabledAndLoggedIn(),
+ sync->sync_initialized(),
+ sync->GetActiveDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES));
+}
+
} // namespace
MostVisitedSites::MostVisitedSites(Profile* profile)
content::URLDataSource::Add(profile_,
new suggestions::SuggestionsSource(profile_));
content::URLDataSource::Add(profile_, new ThumbnailListSource(profile_));
+
+ // Register this class as an observer to the sync service. It is important to
+ // be notified of changes in the sync state such as initialization, sync
+ // being enabled or disabled, etc.
+ ProfileSyncService* profile_sync_service =
+ ProfileSyncServiceFactory::GetForProfile(profile_);
+ if (profile_sync_service)
+ profile_sync_service->AddObserver(this);
}
MostVisitedSites::~MostVisitedSites() {
+ ProfileSyncService* profile_sync_service =
+ ProfileSyncServiceFactory::GetForProfile(profile_);
+ if (profile_sync_service && profile_sync_service->HasObserver(this))
+ profile_sync_service->RemoveObserver(this);
}
void MostVisitedSites::Destroy(JNIEnv* env, jobject obj) {
std::string url_string = ConvertJavaStringToUTF8(env, url);
scoped_refptr<TopSites> top_sites(profile_->GetTopSites());
- // If the Suggestions service is enabled, create a callback to fetch a
- // server thumbnail from it, in case the local thumbnail is not found.
+ // If the Suggestions service is enabled and in use, create a callback to
+ // fetch a server thumbnail from it, in case the local thumbnail is not found.
SuggestionsService* suggestions_service =
SuggestionsServiceFactory::GetForProfile(profile_);
- base::Closure lookup_failed_callback = suggestions_service ?
+ bool use_suggestions_service = suggestions_service &&
+ mv_source_ == SUGGESTIONS_SERVICE;
+ base::Closure lookup_failed_callback = use_suggestions_service ?
base::Bind(&MostVisitedSites::GetSuggestionsThumbnailOnUIThread,
weak_ptr_factory_.GetWeakPtr(),
suggestions_service, url_string,
}
}
+void MostVisitedSites::OnStateChanged() {
+ // There have been changes to the sync state. This class cares about a few
+ // (just initialized, enabled/disabled or history sync state changed). Re-run
+ // the query code which will use the proper state.
+ QueryMostVisitedURLs();
+}
+
// static
bool MostVisitedSites::Register(JNIEnv* env) {
return RegisterNativesImpl(env);
if (suggestions_service) {
// Suggestions service is enabled, initiate a query.
suggestions_service->FetchSuggestionsData(
+ GetSyncState(profile_),
base::Bind(
&MostVisitedSites::OnSuggestionsProfileAvailable,
weak_ptr_factory_.GetWeakPtr(),
#include "base/memory/weak_ptr.h"
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service_observer.h"
#include "components/suggestions/proto/suggestions.pb.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
}
// Provides the list of most visited sites and their thumbnails to Java.
-class MostVisitedSites : public content::NotificationObserver {
+class MostVisitedSites : public ProfileSyncServiceObserver,
+ public content::NotificationObserver {
public:
typedef base::Callback<
void(base::android::ScopedJavaGlobalRef<jobject>* bitmap,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
+ // ProfileSyncServiceObserver implementation.
+ virtual void OnStateChanged() OVERRIDE;
+
// Registers JNI methods.
static bool Register(JNIEnv* env);
did_finish_load);
}
+void TabAndroid::OnWebContentsInstantSupportDisabled(
+ const content::WebContents* contents) {
+ DCHECK(contents);
+ if (web_contents() != contents)
+ return;
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_Tab_onWebContentsInstantSupportDisabled(env,
+ weak_java_tab_.get(env).obj());
+}
+
void TabAndroid::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
WindowAndroidHelper::FromWebContents(web_contents())->
SetWindowAndroid(content_view_core->GetWindowAndroid());
CoreTabHelper::FromWebContents(web_contents())->set_delegate(this);
+ SearchTabHelper::FromWebContents(web_contents())->set_delegate(this);
web_contents_delegate_.reset(
new chrome::android::ChromeWebContentsDelegateAndroid(
env, jweb_contents_delegate));
#include "base/strings/string16.h"
#include "chrome/browser/sessions/session_id.h"
#include "chrome/browser/sync/glue/synced_tab_delegate_android.h"
+#include "chrome/browser/ui/search/search_tab_helper_delegate.h"
#include "chrome/browser/ui/tab_contents/core_tab_helper_delegate.h"
#include "chrome/browser/ui/toolbar/toolbar_model.h"
#include "content/public/browser/notification_observer.h"
}
class TabAndroid : public CoreTabHelperDelegate,
+ public SearchTabHelperDelegate,
public content::NotificationObserver {
public:
enum TabLoadStatus {
bool did_start_load,
bool did_finish_load) OVERRIDE;
+ // Overridden from SearchTabHelperDelegate:
+ virtual void OnWebContentsInstantSupportDisabled(
+ const content::WebContents* web_contents) OVERRIDE;
+
// NotificationObserver -----------------------------------------------------
virtual void Observe(int type,
const content::NotificationSource& source,
WorkAreaChanged());
}
+- (BOOL)application:(NSApplication*)application
+ willContinueUserActivityWithType:(NSString*)userActivityType {
+ return [userActivityType isEqualToString:NSUserActivityTypeBrowsingWeb];
+}
+
+- (BOOL)application:(NSApplication*)application
+ continueUserActivity:(NSUserActivity*)userActivity
+ restorationHandler:(void (^)(NSArray*))restorationHandler {
+ if (![userActivity.activityType
+ isEqualToString:NSUserActivityTypeBrowsingWeb]) {
+ return NO;
+ }
+
+ NSURL* url = userActivity.webPageURL;
+ if (!url)
+ return NO;
+
+ GURL gurl(base::SysNSStringToUTF8([url absoluteString]));
+ std::vector<GURL> gurlVector;
+ gurlVector.push_back(gurl);
+
+ [self openUrls:gurlVector];
+ return YES;
+}
+
+- (void)application:(NSApplication*)application
+ didFailToContinueUserActivityWithType:(NSString*)userActivityType
+ error:(NSError*)error {
+}
+
@end // @implementation AppController
//---------------------------------------------------------------------------
extension_service->GetExtensionById(extension_id, false);
return extension &&
(!profile_->IsOffTheRecord() ||
- !extensions::util::IsIncognitoEnabled(extension_id, profile_));
+ extensions::util::IsIncognitoEnabled(extension_id, profile_));
}
bool KeywordExtensionsDelegateImpl::Start(
"<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\""
" formsignature=\"15916856893790176210\""
" autofillused=\"false\""
- " datapresent=\"1f7e0003780000080014\">"
+ " datapresent=\"1f7e0003780000080004\">"
"<field signature=\"2594484045\" autofilltype=\"2\"/>"
"<field signature=\"2750915947\" autofilltype=\"2\"/>"
"<field signature=\"3494787134\" autofilltype=\"2\"/>"
#else
base::FilePath profile_path =
GetStartupProfilePath(user_data_dir, parsed_command_line);
+
+ // Without NewAvatarMenu, replace guest with any existing profile.
+ if (!switches::IsNewAvatarMenu() &&
+ profile_path == ProfileManager::GetGuestProfilePath()) {
+ profile_path = g_browser_process->profile_manager()->GetProfileInfoCache().
+ GetPathOfProfileAtIndex(0);
+ }
profile = g_browser_process->profile_manager()->GetProfile(
profile_path);
const char kEnabledHttpOnlyGroupName[] = "EnabledHttpOnly";
const char kDisabledAllGroupName[] = "DisabledAll";
- base::StringPiece sdch_trial_group =
+ // Store in a string on return to keep underlying storage for
+ // StringPiece stable.
+ std::string sdch_trial_group_string =
base::FieldTrialList::FindFullName(kSdchFieldTrialName);
+ base::StringPiece sdch_trial_group(sdch_trial_group_string);
if (sdch_trial_group.starts_with(kEnabledAllGroupName)) {
net::SdchManager::EnableSecureSchemeSupport(true);
net::SdchManager::EnableSdchSupport(true);
#include "chrome/browser/chrome_browser_main_linux.h"
+#include <fontconfig/fontconfig.h>
+
#include "chrome/browser/browser_process.h"
#include "components/breakpad/app/breakpad_linux.h"
#include "components/metrics/metrics_service.h"
ChromeBrowserMainPartsLinux::~ChromeBrowserMainPartsLinux() {
}
+void ChromeBrowserMainPartsLinux::ToolkitInitialized() {
+ // Explicitly initialize Fontconfig early on to prevent races later due to
+ // implicit initialization in response to threads' first calls to Fontconfig:
+ // http://crbug.com/404311
+ FcInit();
+
+ ChromeBrowserMainPartsPosix::ToolkitInitialized();
+}
+
void ChromeBrowserMainPartsLinux::PreProfileInit() {
#if !defined(OS_CHROMEOS)
// Needs to be called after we have chrome::DIR_USER_DATA and
virtual ~ChromeBrowserMainPartsLinux();
// ChromeBrowserMainParts overrides.
+ virtual void ToolkitInitialized() OVERRIDE;
virtual void PreProfileInit() OVERRIDE;
virtual void PostProfileInit() OVERRIDE;
}
std::string BootTimesLoader::Stats::SerializeToString() const {
- if (uptime_.empty() || disk_.empty())
+ if (uptime_.empty() && disk_.empty())
return std::string();
base::DictionaryValue dictionary;
dictionary.SetString(kUptime, uptime_);
}
std::vector<linked_ptr<api::file_browser_private::ProfileInfo> >
-GetLoggedInProfileInfoList(content::WebContents* contents) {
+GetLoggedInProfileInfoList() {
DCHECK(user_manager::UserManager::IsInitialized());
const std::vector<Profile*>& profiles =
g_browser_process->profile_manager()->GetLoadedProfiles();
// TODO(hirono): Remove the property from the profile_info.
profile_info->is_current_profile = true;
- // Make an icon URL of the profile.
- if (contents) {
- const gfx::Image& image =
- ash::GetAvatarImageForContext(contents->GetBrowserContext());
- const gfx::ImageSkia& skia = image.AsImageSkia();
- profile_info->profile_image.reset(
- new api::file_browser_private::ImageSet);
- profile_info->profile_image->scale1x_url =
- webui::GetBitmapDataUrl(skia.GetRepresentation(1.0f).sk_bitmap());
- profile_info->profile_image->scale2x_url =
- webui::GetBitmapDataUrl(skia.GetRepresentation(2.0f).sk_bitmap());
- }
result_profiles.push_back(profile_info);
}
bool FileBrowserPrivateGetProfilesFunction::RunSync() {
const std::vector<linked_ptr<api::file_browser_private::ProfileInfo> >&
- profiles = GetLoggedInProfileInfoList(GetAssociatedWebContents());
+ profiles = GetLoggedInProfileInfoList();
// Obtains the display profile ID.
apps::AppWindow* const app_window = GetCurrentAppWindow(this);
using api::file_browser_private::VisitDesktop::Params;
const scoped_ptr<Params> params(Params::Create(*args_));
const std::vector<linked_ptr<api::file_browser_private::ProfileInfo> >&
- profiles = GetLoggedInProfileInfoList(GetAssociatedWebContents());
+ profiles = GetLoggedInProfileInfoList();
chrome::MultiUserWindowManager* const window_manager =
chrome::MultiUserWindowManager::GetInstance();
// Slow tests are disabled on debug build. http://crbug.com/327719
#if !defined(NDEBUG)
-#define MAYBE_PRE_Badge DISABLED_PRE_Badge
-#define MAYBE_Badge DISABLED_Badge
-#else
-#define MAYBE_PRE_Badge PRE_Badge
-#define MAYBE_Badge Badge
-#endif
-IN_PROC_BROWSER_TEST_F(MultiProfileFileManagerBrowserTest, MAYBE_PRE_Badge) {
- AddAllUsers();
-}
-
-IN_PROC_BROWSER_TEST_F(MultiProfileFileManagerBrowserTest, MAYBE_Badge) {
- // Test the profile badge to be correctly shown and hidden.
- set_test_case_name("multiProfileBadge");
- StartTest();
-}
-
-// Slow tests are disabled on debug build. http://crbug.com/327719
-#if !defined(NDEBUG)
#define MAYBE_PRE_VisitDesktopMenu DISABLED_PRE_VisitDesktopMenu
#define MAYBE_VisitDesktopMenu DISABLED_VisitDesktopMenu
#else
::switches::kDisableWebRtcHWDecoding,
::switches::kDisableWebRtcHWEncoding,
::switches::kEnableWebRtcHWVp8Encoding,
+ ::switches::kEnableWebRtcHWH264Encoding,
#endif
::switches::kDisableVaapiAcceleratedVideoEncode,
#if defined(USE_OZONE)
}
void AutoEnrollmentController::StartClient(
- const std::vector<std::string>& state_keys) {
+ const std::vector<std::string>& state_keys, bool first_boot) {
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
policy::DeviceManagementService* service =
std::string device_id;
if (GetMode() == MODE_FORCED_RE_ENROLLMENT) {
retrieve_device_state = true;
- device_id = state_keys.empty() ? std::string() : state_keys.front();
+ if (!state_keys.empty() && !first_boot)
+ device_id = state_keys.front();
} else {
device_id = policy::DeviceCloudPolicyManagerChromeOS::GetMachineID();
}
DeviceSettingsService::OwnershipStatus status);
// Starts the auto-enrollment client.
- void StartClient(const std::vector<std::string>& state_keys);
+ void StartClient(const std::vector<std::string>& state_keys, bool first_boot);
// Sets |state_| and notifies |progress_callbacks_|.
void UpdateState(policy::AutoEnrollmentState state);
login_performer_->set_delegate(NULL);
ignore_result(login_performer_.release());
+ // Will call OnProfilePrepared() in the end.
+ LoginUtils::Get()->PrepareProfile(user_context,
+ has_auth_cookies,
+ false, // Start session for user.
+ this);
+
// Update user's displayed email.
if (!display_email_.empty()) {
user_manager::UserManager::Get()->SaveUserDisplayEmail(
user_context.GetUserID(), display_email_);
display_email_.clear();
}
-
- // Will call OnProfilePrepared() in the end.
- LoginUtils::Get()->PrepareProfile(user_context,
- has_auth_cookies,
- false, // Start session for user.
- this);
}
void ExistingUserController::OnProfilePrepared(Profile* profile) {
}
}
- if (new_recommended_locales.empty()) {
- // There are no recommended locales.
- PublicSessionRecommendedLocaleMap::iterator it =
- public_session_recommended_locales_.find(user_id);
- if (it != public_session_recommended_locales_.end()) {
- // If there previously were recommended locales, remove them from
- // |public_session_recommended_locales_| and notify the UI.
- public_session_recommended_locales_.erase(it);
- SetPublicSessionLocales(user_id, &new_recommended_locales);
- }
- return;
- }
-
- // There are recommended locales.
std::vector<std::string>& recommended_locales =
public_session_recommended_locales_[user_id];
- if (new_recommended_locales != recommended_locales) {
- // If the list of recommended locales has changed, update
- // |public_session_recommended_locales_| and notify the UI.
+
+ if (new_recommended_locales != recommended_locales)
+ SetPublicSessionLocales(user_id, new_recommended_locales);
+
+ if (new_recommended_locales.empty())
+ public_session_recommended_locales_.erase(user_id);
+ else
recommended_locales = new_recommended_locales;
- SetPublicSessionLocales(user_id, &new_recommended_locales);
- }
}
void ChromeUserSelectionScreen::SetPublicSessionDisplayName(
void ChromeUserSelectionScreen::SetPublicSessionLocales(
const std::string& user_id,
- const std::vector<std::string>* recommended_locales) {
+ const std::vector<std::string>& recommended_locales) {
if (!handler_initialized_)
return;
// Construct the list of available locales. This list consists of the
// recommended locales, followed by all others.
- scoped_ptr<base::ListValue> locales =
- GetUILanguageList(recommended_locales, std::string());
+ scoped_ptr<base::ListValue> available_locales =
+ GetUILanguageList(&recommended_locales, std::string());
- // Set the initially selected locale. If the list of recommended locales is
- // not empty, select its first entry. Otherwise, select the current UI locale.
- const std::string& default_locale = recommended_locales->empty() ?
- g_browser_process->GetApplicationLocale() : recommended_locales->front();
+ // Set the initially selected locale to the first recommended locale that is
+ // actually available or the current UI locale if none of them are available.
+ const std::string default_locale = FindMostRelevantLocale(
+ recommended_locales,
+ *available_locales.get(),
+ g_browser_process->GetApplicationLocale());
// Set a flag to indicate whether the list of recommended locales contains at
// least two entries. This is used to decide whether the public session pod
// expands to its basic form (for zero or one recommended locales) or the
// advanced form (two or more recommended locales).
- const bool two_or_more_recommended_locales = recommended_locales->size() >= 2;
+ const bool two_or_more_recommended_locales = recommended_locales.size() >= 2;
// Notify the UI.
handler_->SetPublicSessionLocales(user_id,
- locales.Pass(),
+ available_locales.Pass(),
default_locale,
two_or_more_recommended_locales);
}
// of the |recommended_locales| followed by all other available locales.
void SetPublicSessionLocales(
const std::string& user_id,
- const std::vector<std::string>* recommended_locales);
+ const std::vector<std::string>& recommended_locales);
bool handler_initialized_;
#include "chrome/browser/chromeos/login/screens/user_selection_screen.h"
-#include <vector>
-
#include "ash/shell.h"
#include "base/location.h"
#include "base/logging.h"
}
std::vector<std::string> kEmptyRecommendedLocales;
- const std::vector<std::string>* recommended_locales =
+ const std::vector<std::string>& recommended_locales =
public_session_recommended_locales ?
- public_session_recommended_locales : &kEmptyRecommendedLocales;
-
- // Set |kKeyInitialLocales| to the list of available locales. This list
- // consists of the recommended locales, followed by all others.
- user_dict->Set(
- kKeyInitialLocales,
- GetUILanguageList(recommended_locales, std::string()).release());
-
- // Set |kKeyInitialLocale| to the initially selected locale. If the list of
- // recommended locales is not empty, select its first entry. Otherwise,
- // select the current UI locale.
- user_dict->SetString(kKeyInitialLocale,
- recommended_locales->empty() ?
- g_browser_process->GetApplicationLocale() :
- recommended_locales->front());
+ *public_session_recommended_locales : kEmptyRecommendedLocales;
+
+ // Construct the list of available locales. This list consists of the
+ // recommended locales, followed by all others.
+ scoped_ptr<base::ListValue> available_locales =
+ GetUILanguageList(&recommended_locales, std::string());
+
+ // Select the the first recommended locale that is actually available or the
+ // current UI locale if none of them are available.
+ const std::string selected_locale = FindMostRelevantLocale(
+ recommended_locales,
+ *available_locales.get(),
+ g_browser_process->GetApplicationLocale());
+
+ // Set |kKeyInitialLocales| to the list of available locales.
+ user_dict->Set(kKeyInitialLocales, available_locales.release());
+
+ // Set |kKeyInitialLocale| to the initially selected locale.
+ user_dict->SetString(kKeyInitialLocale, selected_locale);
// Set |kKeyInitialMultipleRecommendedLocales| to indicate whether the list
// of recommended locales contains at least two entries. This is used to
// or one recommended locales) or the advanced form (two or more recommended
// locales).
user_dict->SetBoolean(kKeyInitialMultipleRecommendedLocales,
- recommended_locales->size() >= 2);
+ recommended_locales.size() >= 2);
// Set |kKeyInitialKeyboardLayout| to the current keyboard layout. This
// value will be used temporarily only because the UI immediately requests a
void UserSelectionScreen::SetAuthType(
const std::string& username,
ScreenlockBridge::LockHandler::AuthType auth_type) {
+ DCHECK(GetAuthType(username) !=
+ ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD ||
+ auth_type == ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD);
user_auth_type_map_[username] = auth_type;
}
// Users with a policy that prevents them being added to a session will be
// shown in login UI but will be grayed out.
// Same applies to owner account (see http://crbug.com/385034).
- if (check == MultiProfileUserController::ALLOWED ||
- check == MultiProfileUserController::NOT_ALLOWED_POLICY_FORBIDS ||
- check == MultiProfileUserController::NOT_ALLOWED_OWNER_AS_SECONDARY ||
- check ==
- MultiProfileUserController::NOT_ALLOWED_POLICY_CERT_TAINTED) {
- result.push_back(*it);
- }
+ result.push_back(*it);
}
}
#include "net/url_request/url_request_status.h"
#include "policy/policy_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/icu/source/common/unicode/locid.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/window_open_disposition.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/widget/widget.h"
#include "url/gurl.h"
-//#include "third_party/cros_system_api/dbus/service_constants.h"
namespace em = enterprise_management;
"fr",
"nl",
};
+const char* kInvalidRecommendedLocale[] = {
+ "xx",
+};
const char kPublicSessionLocale[] = "de";
const char kPublicSessionInputMethodIDTemplate[] = "_comp_ime_%sxkb:de:neo:ger";
return user_manager::UserManager::Get()->IsSessionStarted();
}
-// GetKeyboardLayoutsForLocale() posts a task to a background task runner. This
-// method flushes that task runner and the current thread's message loop to
-// ensure that GetKeyboardLayoutsForLocale() is finished.
-void WaitForGetKeyboardLayoutsForLocaleToFinish() {
- base::SequencedWorkerPool* worker_pool =
- content::BrowserThread::GetBlockingPool();
- scoped_refptr<base::SequencedTaskRunner> background_task_runner =
- worker_pool->GetSequencedTaskRunner(
- worker_pool->GetNamedSequenceToken(kSequenceToken));
- base::RunLoop run_loop;
- background_task_runner->PostTaskAndReply(FROM_HERE,
- base::Bind(&base::DoNothing),
- run_loop.QuitClosure());
- run_loop.Run();
- base::RunLoop().RunUntilIdle();
-}
-
} // namespace
class DeviceLocalAccountTest : public DevicePolicyCrosBrowserTest,
virtual void SetUpOnMainThread() OVERRIDE {
DevicePolicyCrosBrowserTest::SetUpOnMainThread();
+ initial_locale_ = g_browser_process->GetApplicationLocale();
+ initial_language_ = l10n_util::GetLanguage(initial_locale_);
+
content::WindowedNotificationObserver(
chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
content::NotificationService::AllSources()).Wait();
WaitForDisplayName(user_id_1_, kDisplayName1);
}
+ void ExpandPublicSessionPod(bool expect_advanced) {
+ bool advanced = false;
+ // Click on the pod to expand it.
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ contents_,
+ base::StringPrintf(
+ "var pod ="
+ " document.getElementById('pod-row').getPodWithUsername_('%s');"
+ "pod.click();"
+ "domAutomationController.send(pod.classList.contains('advanced'));",
+ user_id_1_.c_str()),
+ &advanced));
+ // Verify that the pod expanded to its basic/advanced form, as expected.
+ EXPECT_EQ(expect_advanced, advanced);
+
+ // Verify that the construction of the pod's language list did not affect
+ // the current ICU locale.
+ EXPECT_EQ(initial_language_, icu::Locale::getDefault().getLanguage());
+ }
+
+ // GetKeyboardLayoutsForLocale() posts a task to a background task runner.
+ // This method flushes that task runner and the current thread's message loop
+ // to ensure that GetKeyboardLayoutsForLocale() is finished.
+ void WaitForGetKeyboardLayoutsForLocaleToFinish() {
+ base::SequencedWorkerPool* worker_pool =
+ content::BrowserThread::GetBlockingPool();
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner =
+ worker_pool->GetSequencedTaskRunner(
+ worker_pool->GetNamedSequenceToken(kSequenceToken));
+ base::RunLoop run_loop;
+ background_task_runner->PostTaskAndReply(FROM_HERE,
+ base::Bind(&base::DoNothing),
+ run_loop.QuitClosure());
+ run_loop.Run();
+ base::RunLoop().RunUntilIdle();
+
+ // Verify that the construction of the keyboard layout list did not affect
+ // the current ICU locale.
+ EXPECT_EQ(initial_language_, icu::Locale::getDefault().getLanguage());
+ }
+
void StartLogin(const std::string& locale,
const std::string& input_method) {
// Start login into the device-local account.
const std::string user_id_2_;
const std::string public_session_input_method_id_;
+ std::string initial_locale_;
+ std::string initial_language_;
+
scoped_ptr<base::RunLoop> run_loop_;
UserPolicyBuilder device_local_account_policy_;
};
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, NoRecommendedLocaleNoSwitch) {
- const std::string initial_locale = g_browser_process->GetApplicationLocale();
-
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
WaitForPolicy();
- // Click on the pod to expand it. Verify that the pod expands to its basic
- // form as there are no recommended locales.
- bool advanced = false;
- ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
- contents_,
- base::StringPrintf(
- "var pod ="
- " document.getElementById('pod-row').getPodWithUsername_('%s');"
- "pod.click();"
- "domAutomationController.send(pod.classList.contains('advanced'));",
- user_id_1_.c_str()),
- &advanced));
- EXPECT_FALSE(advanced);
+ ExpandPublicSessionPod(false);
// Click the enter button to start the session.
ASSERT_TRUE(content::ExecuteScript(
// Verify that the locale has not changed and the first keyboard layout
// applicable to the locale was chosen.
- EXPECT_EQ(initial_locale, g_browser_process->GetApplicationLocale());
+ EXPECT_EQ(initial_locale_, g_browser_process->GetApplicationLocale());
+ EXPECT_EQ(initial_language_, icu::Locale::getDefault().getLanguage());
VerifyKeyboardLayoutMatchesLocale();
}
WaitForPolicy();
- // Click on the pod to expand it. Verify that the pod expands to its basic
- // form as there are no recommended locales.
- bool advanced = false;
- ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
- contents_,
- base::StringPrintf(
- "var pod ="
- " document.getElementById('pod-row').getPodWithUsername_('%s');"
- "pod.click();"
- "domAutomationController.send(pod.classList.contains('advanced'));",
- user_id_1_.c_str()),
- &advanced));
- EXPECT_FALSE(advanced);
+ ExpandPublicSessionPod(false);
// Click the link that switches the pod to its advanced form. Verify that the
// pod switches from basic to advanced.
+ bool advanced = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
contents_,
base::StringPrintf(
// Verify that the locale and keyboard layout have been applied.
EXPECT_EQ(kPublicSessionLocale, g_browser_process->GetApplicationLocale());
+ EXPECT_EQ(l10n_util::GetLanguage(kPublicSessionLocale),
+ icu::Locale::getDefault().getLanguage());
EXPECT_EQ(public_session_input_method_id_,
chromeos::input_method::InputMethodManager::Get()->
GetCurrentInputMethod().id());
WaitForPolicy();
- // Click on the pod to expand it. Verify that the pod expands to its basic
- // form as there is only one recommended locale.
- bool advanced = false;
- ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
- contents_,
- base::StringPrintf(
- "var pod ="
- " document.getElementById('pod-row').getPodWithUsername_('%s');"
- "pod.click();"
- "domAutomationController.send(pod.classList.contains('advanced'));",
- user_id_1_.c_str()),
- &advanced));
- EXPECT_FALSE(advanced);
+ ExpandPublicSessionPod(false);
// Click the enter button to start the session.
ASSERT_TRUE(content::ExecuteScript(
// layout applicable to the locale was chosen.
EXPECT_EQ(kSingleRecommendedLocale[0],
g_browser_process->GetApplicationLocale());
+ EXPECT_EQ(l10n_util::GetLanguage(kSingleRecommendedLocale[0]),
+ icu::Locale::getDefault().getLanguage());
VerifyKeyboardLayoutMatchesLocale();
}
WaitForPolicy();
- // Click on the pod to expand it. Verify that the pod expands to its advanced
- // form directly as there are two or more recommended locales.
- bool advanced = false;
- ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
- contents_,
- base::StringPrintf(
- "var pod ="
- " document.getElementById('pod-row').getPodWithUsername_('%s');"
- "pod.click();"
- "domAutomationController.send(pod.classList.contains('advanced'));",
- user_id_1_.c_str()),
- &advanced));
- EXPECT_TRUE(advanced);
+ ExpandPublicSessionPod(true);
// Verify that the pod shows a list of locales beginning with the recommended
// ones, followed by others.
const base::DictionaryValue* state = NULL;
ASSERT_TRUE(value_ptr);
ASSERT_TRUE(value_ptr->GetAsDictionary(&state));
+ bool advanced = false;
EXPECT_TRUE(state->GetBoolean("advanced", &advanced));
EXPECT_TRUE(advanced);
EXPECT_TRUE(state->GetString("locale", &selected_locale));
// Verify that the locale and keyboard layout have been applied.
EXPECT_EQ(kPublicSessionLocale, g_browser_process->GetApplicationLocale());
+ EXPECT_EQ(l10n_util::GetLanguage(kPublicSessionLocale),
+ icu::Locale::getDefault().getLanguage());
EXPECT_EQ(public_session_input_method_id_,
chromeos::input_method::InputMethodManager::Get()->
GetCurrentInputMethod().id());
}
+IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, InvalidRecommendedLocale) {
+ // Specify an invalid recommended locale.
+ SetRecommendedLocales(kInvalidRecommendedLocale,
+ arraysize(kInvalidRecommendedLocale));
+ UploadAndInstallDeviceLocalAccountPolicy();
+ AddPublicSessionToDevicePolicy(kAccountId1);
+
+ WaitForPolicy();
+
+ // Click on the pod to expand it. Verify that the pod expands to its basic
+ // form as there is only one recommended locale.
+ bool advanced = false;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+ contents_,
+ base::StringPrintf(
+ "var pod ="
+ " document.getElementById('pod-row').getPodWithUsername_('%s');"
+ "pod.click();"
+ "domAutomationController.send(pod.classList.contains('advanced'));",
+ user_id_1_.c_str()),
+ &advanced));
+ EXPECT_FALSE(advanced);
+ EXPECT_EQ(l10n_util::GetLanguage(initial_locale_),
+ icu::Locale::getDefault().getLanguage());
+
+ // Click the enter button to start the session.
+ ASSERT_TRUE(content::ExecuteScript(
+ contents_,
+ base::StringPrintf(
+ "document.getElementById('pod-row').getPodWithUsername_('%s')"
+ " .querySelector('.enter-button').click();",
+ user_id_1_.c_str())));
+
+ WaitForSessionStart();
+
+ // Verify that since the recommended locale was invalid, the locale has not
+ // changed and the first keyboard layout applicable to the locale was chosen.
+ EXPECT_EQ(initial_locale_, g_browser_process->GetApplicationLocale());
+ EXPECT_EQ(l10n_util::GetLanguage(initial_locale_),
+ icu::Locale::getDefault().getLanguage());
+ VerifyKeyboardLayoutMatchesLocale();
+}
+
IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest,
AutoLoginWithoutRecommendedLocales) {
- const std::string initial_locale = g_browser_process->GetApplicationLocale();
-
UploadAndInstallDeviceLocalAccountPolicy();
AddPublicSessionToDevicePolicy(kAccountId1);
EnableAutoLogin();
// Verify that the locale has not changed and the first keyboard layout
// applicable to the locale was chosen.
- EXPECT_EQ(initial_locale, g_browser_process->GetApplicationLocale());
+ EXPECT_EQ(initial_locale_, g_browser_process->GetApplicationLocale());
+ EXPECT_EQ(initial_language_, icu::Locale::getDefault().getLanguage());
VerifyKeyboardLayoutMatchesLocale();
}
// Verify that the first recommended locale has been applied and the first
// keyboard layout applicable to the locale was chosen.
EXPECT_EQ(kRecommendedLocales1[0], g_browser_process->GetApplicationLocale());
+ EXPECT_EQ(l10n_util::GetLanguage(kRecommendedLocales1[0]),
+ icu::Locale::getDefault().getLanguage());
VerifyKeyboardLayoutMatchesLocale();
}
// Verify that the locale and keyboard layout have been applied.
EXPECT_EQ(kPublicSessionLocale, g_browser_process->GetApplicationLocale());
+ EXPECT_EQ(l10n_util::GetLanguage(kPublicSessionLocale),
+ icu::Locale::getDefault().getLanguage());
EXPECT_EQ(public_session_input_method_id_,
chromeos::input_method::InputMethodManager::Get()->
GetCurrentInputMethod().id());
// Verify that the locale and keyboard layout are still in force.
EXPECT_EQ(kPublicSessionLocale, g_browser_process->GetApplicationLocale());
+ EXPECT_EQ(l10n_util::GetLanguage(kPublicSessionLocale),
+ icu::Locale::getDefault().getLanguage());
EXPECT_EQ(public_session_input_method_id_,
chromeos::input_method::InputMethodManager::Get()->
GetCurrentInputMethod().id());
}
void EnrollmentHandlerChromeOS::CheckStateKeys(
- const std::vector<std::string>& state_keys) {
+ const std::vector<std::string>& state_keys, bool /* first_boot */) {
CHECK_EQ(STEP_STATE_KEYS, enrollment_step_);
// Make sure state keys are available if forced re-enrollment is on.
};
// Handles the response to a request for server-backed state keys.
- void CheckStateKeys(const std::vector<std::string>& state_keys);
+ void CheckStateKeys(const std::vector<std::string>& state_keys,
+ bool first_boot);
// Starts registration if the store is initialized.
void AttemptRegistration();
scoped_refptr<base::TaskRunner> delayed_task_runner)
: session_manager_client_(session_manager_client),
delayed_task_runner_(delayed_task_runner),
+ first_boot_(false),
requested_(false),
initial_retrieval_completed_(false),
weak_factory_(this) {
}
if (!callback.is_null())
- callback.Run(state_keys_);
+ callback.Run(state_keys_, first_boot_);
return;
}
}
void ServerBackedStateKeysBroker::StoreStateKeys(
- const std::vector<std::string>& state_keys) {
+ const std::vector<std::string>& state_keys, bool first_boot) {
bool send_notification = !initial_retrieval_completed_;
requested_ = false;
} else {
send_notification |= state_keys_ != state_keys;
state_keys_ = state_keys;
+ first_boot_ = first_boot;
}
if (send_notification)
callback != callbacks.end();
++callback) {
if (!callback->is_null())
- callback->Run(state_keys_);
+ callback->Run(state_keys_, first_boot_);
}
delayed_task_runner_->PostDelayedTask(
class ServerBackedStateKeysBroker {
public:
typedef scoped_ptr<base::CallbackList<void()>::Subscription> Subscription;
- typedef base::Callback<void(const std::vector<std::string>&)>
+ typedef base::Callback<void(const std::vector<std::string>&, bool)>
StateKeysCallback;
ServerBackedStateKeysBroker(
void FetchStateKeys();
// Stores newly-received state keys and notifies consumers.
- void StoreStateKeys(const std::vector<std::string>& state_keys);
+ void StoreStateKeys(const std::vector<std::string>& state_keys,
+ bool first_boot);
chromeos::SessionManagerClient* session_manager_client_;
// The current set of state keys.
std::vector<std::string> state_keys_;
+ // Set to true on first run after factory reset.
+ bool first_boot_;
+
// Whether a request for state keys is pending.
bool requested_;
ServerBackedStateKeysBrokerTest()
: task_runner_(new base::TestSimpleTaskRunner()),
broker_(&fake_session_manager_client_, task_runner_),
+ first_boot_(false),
updated_(false),
callback_invoked_(false) {
state_keys_.push_back("1");
EXPECT_EQ(state_keys_.front(), broker_.current_state_key());
}
- void HandleStateKeysCallback(const std::vector<std::string>& state_keys) {
+ void HandleStateKeysCallback(const std::vector<std::string>& state_keys,
+ bool first_boot) {
callback_invoked_ = true;
callback_state_keys_ = state_keys;
+ first_boot_ = first_boot;
}
protected:
chromeos::FakeSessionManagerClient fake_session_manager_client_;
ServerBackedStateKeysBroker broker_;
std::vector<std::string> state_keys_;
+ bool first_boot_;
bool updated_;
std::vector<std::string> callback_state_keys_;
bool callback_invoked_;
base::RunLoop().RunUntilIdle();
ExpectGood();
EXPECT_TRUE(callback_invoked_);
+ EXPECT_FALSE(first_boot_);
EXPECT_EQ(state_keys_, callback_state_keys_);
}
new base::StringValue("primary-only"),
NULL);
}
+
+ // Set EasyUnlockAllowed policy to false by default for managed accounts.
+ if (store()->has_policy() &&
+ !policy_map->Get(key::kEasyUnlockAllowed)) {
+ policy_map->Set(key::kEasyUnlockAllowed,
+ POLICY_LEVEL_MANDATORY,
+ POLICY_SCOPE_USER,
+ new base::FundamentalValue(false),
+ NULL);
+ }
}
void UserCloudPolicyManagerChromeOS::FetchPolicyOAuthTokenUsingSigninProfile() {
POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
new base::StringValue("primary-only"),
NULL);
+ policy_map_.Set(key::kEasyUnlockAllowed,
+ POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+ new base::FundamentalValue(false),
+ NULL);
expected_bundle_.Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()))
.CopyFrom(policy_map_);
if (argv)
AddTPControlArguments("tapdrag", tap_dragging_.value(), argv);
}
- if (natural_scroll_.Update(settings.natural_scroll_)) {
+ natural_scroll_.Update(settings.natural_scroll_);
+ // Always send natural scrolling to the shell command, as a workaround.
+ // See crbug.com/406480
+ if (natural_scroll_.is_set()) {
updated = true;
if (argv)
AddTPControlArguments("australian_scrolling", natural_scroll_.value(),
#include "base/memory/weak_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
-#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/common/chrome_switches.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/debug_daemon_client.h"
+#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h"
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (succeeded) {
SystemLogsResponse* response = new SystemLogsResponse;
- std::vector<Profile*> last_used = ProfileManager::GetLastOpenedProfiles();
-
- if (last_used.empty() && user_manager::UserManager::IsInitialized() &&
- user_manager::UserManager::Get()->IsLoggedInAsKioskApp()) {
- // Use the kiosk app profile explicitly because kiosk session does not
- // open any browsers thus ProfileManager::GetLastOpenedProfiles returns
- // an empty |last_used|.
- last_used.push_back(ProfileManager::GetActiveUserProfile());
+
+ const user_manager::UserList& users =
+ user_manager::UserManager::Get()->GetLoggedInUsers();
+ std::vector<base::FilePath> profile_dirs;
+ for (user_manager::UserList::const_iterator it = users.begin();
+ it != users.end();
+ ++it) {
+ if ((*it)->username_hash().empty())
+ continue;
+ profile_dirs.push_back(
+ chromeos::ProfileHelper::GetProfilePathByUserIdHash(
+ (*it)->username_hash()));
}
content::BrowserThread::PostBlockingPoolTaskAndReply(
FROM_HERE,
base::Bind(&DebugDaemonLogSource::ReadUserLogFiles,
- user_log_files, last_used, response),
+ user_log_files, profile_dirs, response),
base::Bind(&DebugDaemonLogSource::MergeResponse,
weak_ptr_factory_.GetWeakPtr(),
base::Owned(response)));
// static
void DebugDaemonLogSource::ReadUserLogFiles(
const KeyValueMap& user_log_files,
- const std::vector<Profile*>& last_used_profiles,
+ const std::vector<base::FilePath>& profile_dirs,
SystemLogsResponse* response) {
- for (size_t i = 0; i < last_used_profiles.size(); ++i) {
+ for (size_t i = 0; i < profile_dirs.size(); ++i) {
std::string profile_prefix = "Profile[" + base::UintToString(i) + "] ";
for (KeyValueMap::const_iterator it = user_log_files.begin();
it != user_log_files.end();
std::string key = it->first;
std::string value;
std::string filename = it->second;
- base::FilePath profile_dir = last_used_profiles[i]->GetPath();
bool read_success = base::ReadFileToString(
- profile_dir.Append(filename), &value);
+ profile_dirs[i].Append(filename), &value);
if (read_success && !value.empty())
(*response)[profile_prefix + key] = value;
#include <string>
#include <vector>
+#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/feedback/system_logs/system_logs_fetcher_base.h"
// the response parameter.
static void ReadUserLogFiles(
const KeyValueMap& user_log_files,
- const std::vector<Profile*>& last_used_profiles,
+ const std::vector<base::FilePath>& profile_dirs,
SystemLogsResponse* response);
// Merge the responses from ReadUserLogFiles into the main response dict and
plugin_info->is_out_of_process = true;
plugin_info->path = path;
plugin_info->name = kWidevineCdmDisplayName;
- plugin_info->description = kWidevineCdmDescription;
+ plugin_info->description = kWidevineCdmDescription +
+ std::string(" (version: ") + version.GetString() +
+ ")";
plugin_info->version = version.GetString();
content::WebPluginMimeType widevine_cdm_mime_type(
kWidevineCdmPluginMimeType,
const char kHostTransportCommand[] = "host:transport:%s";
const char kLocalhost[] = "127.0.0.1";
+typedef base::Callback<void(int, const std::string&)> CommandCallback;
+typedef base::Callback<void(int, net::StreamSocket*)> SocketCallback;
+
std::string EncodeMessage(const std::string& message) {
static const char kHexChars[] = "0123456789ABCDEF";
void OnSocketAvailable(int result, const std::string& response) {
if (!CheckNetResultOrDie(result))
return;
- callback_.Run(net::OK, socket_.Pass());
+ callback_.Run(net::OK, socket_.release());
delete this;
}
bool CheckNetResultOrDie(int result) {
if (result >= 0)
return true;
- callback_.Run(result, make_scoped_ptr<net::StreamSocket>(NULL));
+ callback_.Run(result, NULL);
delete this;
return false;
}
public:
typedef base::Callback<void(int, const std::string&)> CommandCallback;
typedef base::Callback<void(int result,
- scoped_ptr<net::StreamSocket>)> SocketCallback;
+ net::StreamSocket*)> SocketCallback;
static void AdbQuery(int port,
const std::string& query,
scoped_refptr<base::MessageLoopProxy> response_message_loop,
const AndroidDeviceManager::SocketCallback& callback,
int result,
- scoped_ptr<net::StreamSocket> socket) {
- response_message_loop->PostTask(
- FROM_HERE, base::Bind(callback, result, base::Passed(&socket)));
+ net::StreamSocket* socket) {
+ response_message_loop->PostTask(FROM_HERE,
+ base::Bind(callback, result, socket));
}
class HttpRequest {
typedef AndroidDeviceManager::SocketCallback SocketCallback;
static void CommandRequest(const std::string& request,
- const CommandCallback& callback,
- int result,
- scoped_ptr<net::StreamSocket> socket) {
+ const CommandCallback& callback,
+ int result,
+ net::StreamSocket* socket) {
if (result != net::OK) {
callback.Run(result, std::string());
return;
}
- new HttpRequest(socket.Pass(), request, callback);
+ new HttpRequest(socket, request, callback);
}
static void SocketRequest(const std::string& request,
- const SocketCallback& callback,
- int result,
- scoped_ptr<net::StreamSocket> socket) {
+ const SocketCallback& callback,
+ int result,
+ net::StreamSocket* socket) {
if (result != net::OK) {
- callback.Run(result, make_scoped_ptr<net::StreamSocket>(NULL));
+ callback.Run(result, NULL);
return;
}
- new HttpRequest(socket.Pass(), request, callback);
+ new HttpRequest(socket, request, callback);
}
private:
- HttpRequest(scoped_ptr<net::StreamSocket> socket,
+ HttpRequest(net::StreamSocket* socket,
const std::string& request,
const CommandCallback& callback)
- : socket_(socket.Pass()),
- command_callback_(callback),
- body_pos_(0) {
+ : socket_(socket), command_callback_(callback), body_pos_(0) {
SendRequest(request);
}
- HttpRequest(scoped_ptr<net::StreamSocket> socket,
- const std::string& request,
- const SocketCallback& callback)
- : socket_(socket.Pass()),
+ HttpRequest(net::StreamSocket* socket,
+ const std::string& request,
+ const SocketCallback& callback)
+ : socket_(socket),
socket_callback_(callback),
body_pos_(0) {
SendRequest(request);
if (!command_callback_.is_null())
command_callback_.Run(net::OK, response_.substr(body_pos_));
else
- socket_callback_.Run(net::OK, socket_.Pass());
+ socket_callback_.Run(net::OK, socket_.release());
delete this;
return;
}
if (!command_callback_.is_null())
command_callback_.Run(result, std::string());
else
- socket_callback_.Run(result, make_scoped_ptr<net::StreamSocket>(NULL));
+ socket_callback_.Run(result, NULL);
delete this;
return false;
}
public base::NonThreadSafe {
public:
typedef base::Callback<void(int, const std::string&)> CommandCallback;
- typedef base::Callback<void(int result, scoped_ptr<net::StreamSocket>)>
- SocketCallback;
+ typedef base::Callback<void(int result, net::StreamSocket*)> SocketCallback;
typedef base::Callback<void(const std::vector<std::string>&)> SerialsCallback;
struct BrowserInfo {
typedef base::Callback<void(const DeviceInfo&)> DeviceInfoCallback;
- class AndroidWebSocket {
+ class AndroidWebSocket : public base::RefCountedThreadSafe<AndroidWebSocket> {
public:
class Delegate {
public:
virtual void OnSocketOpened() = 0;
virtual void OnFrameRead(const std::string& message) = 0;
- virtual void OnSocketClosed() = 0;
+ virtual void OnSocketClosed(bool closed_by_device) = 0;
protected:
virtual ~Delegate() {}
};
- virtual ~AndroidWebSocket() {}
+ AndroidWebSocket() {}
+ virtual void Connect() = 0;
+ virtual void Disconnect() = 0;
virtual void SendFrame(const std::string& message) = 0;
+ virtual void ClearDelegate() = 0;
+
+ protected:
+ virtual ~AndroidWebSocket() {}
+
+ private:
+ friend class base::RefCountedThreadSafe<AndroidWebSocket>;
+
+ DISALLOW_COPY_AND_ASSIGN(AndroidWebSocket);
};
class DeviceProvider;
const std::string& url,
const SocketCallback& callback);
- AndroidWebSocket* CreateWebSocket(
+ scoped_refptr<AndroidWebSocket> CreateWebSocket(
const std::string& socket_name,
const std::string& url,
AndroidWebSocket::Delegate* delegate);
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/rand_util.h"
#include "chrome/browser/devtools/device/android_device_manager.h"
const int kBufferSize = 16 * 1024;
-class WebSocketImpl {
+class WebSocketImpl : public AndroidDeviceManager::AndroidWebSocket {
public:
- typedef AndroidDeviceManager::AndroidWebSocket::Delegate Delegate;
-
- WebSocketImpl(Delegate* delegate,
- scoped_ptr<net::StreamSocket> socket);
- void StartListening();
- void SendFrame(const std::string& message);
-
- private:
- void OnBytesRead(scoped_refptr<net::IOBuffer> response_buffer, int result);
- void SendPendingRequests(int result);
- void Disconnect();
-
- Delegate* delegate_;
- scoped_ptr<net::StreamSocket> socket_;
- std::string response_buffer_;
- std::string request_buffer_;
- base::ThreadChecker thread_checker_;
- DISALLOW_COPY_AND_ASSIGN(WebSocketImpl);
-};
-
-class DelegateWrapper
- : public AndroidDeviceManager::AndroidWebSocket::Delegate {
- public:
- DelegateWrapper(base::WeakPtr<Delegate> weak_delegate,
- scoped_refptr<base::MessageLoopProxy> message_loop)
- : weak_delegate_(weak_delegate),
- message_loop_(message_loop) {
- }
-
- virtual ~DelegateWrapper() {}
-
- // AndroidWebSocket::Delegate implementation
- virtual void OnSocketOpened() OVERRIDE {
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&Delegate::OnSocketOpened, weak_delegate_));
- }
-
- virtual void OnFrameRead(const std::string& message) OVERRIDE {
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&Delegate::OnFrameRead, weak_delegate_, message));
- }
-
- virtual void OnSocketClosed() OVERRIDE {
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&Delegate::OnSocketClosed, weak_delegate_));
- }
+ typedef AndroidDeviceManager::Device Device;
+ WebSocketImpl(scoped_refptr<base::MessageLoopProxy> device_message_loop,
+ scoped_refptr<Device> device,
+ const std::string& socket_name,
+ const std::string& url,
+ Delegate* delegate);
+
+ virtual void Connect() OVERRIDE;
+ virtual void Disconnect() OVERRIDE;
+ virtual void SendFrame(const std::string& message) OVERRIDE;
+ virtual void ClearDelegate() OVERRIDE;
private:
- base::WeakPtr<Delegate> weak_delegate_;
- scoped_refptr<base::MessageLoopProxy> message_loop_;
-};
+ friend class base::RefCountedThreadSafe<AndroidWebSocket>;
-class AndroidWebSocketImpl
- : public AndroidDeviceManager::AndroidWebSocket,
- public AndroidDeviceManager::AndroidWebSocket::Delegate {
- public:
- typedef AndroidDeviceManager::Device Device;
- AndroidWebSocketImpl(
- scoped_refptr<base::MessageLoopProxy> device_message_loop,
- scoped_refptr<Device> device,
- const std::string& socket_name,
- const std::string& url,
- AndroidWebSocket::Delegate* delegate);
+ virtual ~WebSocketImpl();
- virtual ~AndroidWebSocketImpl();
-
- // AndroidWebSocket implementation
- virtual void SendFrame(const std::string& message) OVERRIDE;
+ void Connected(int result, net::StreamSocket* socket);
+ void StartListeningOnHandlerThread();
+ void OnBytesRead(scoped_refptr<net::IOBuffer> response_buffer, int result);
+ void SendFrameOnHandlerThread(const std::string& message);
+ void SendPendingRequests(int result);
+ void DisconnectOnHandlerThread(bool closed_by_device);
- // AndroidWebSocket::Delegate implementation
- virtual void OnSocketOpened() OVERRIDE;
- virtual void OnFrameRead(const std::string& message) OVERRIDE;
- virtual void OnSocketClosed() OVERRIDE;
-
- private:
- void Connected(int result, scoped_ptr<net::StreamSocket> socket);
+ void OnSocketOpened();
+ void OnFrameRead(const std::string& message);
+ void OnSocketClosed(bool closed_by_device);
scoped_refptr<base::MessageLoopProxy> device_message_loop_;
scoped_refptr<Device> device_;
std::string socket_name_;
std::string url_;
- WebSocketImpl* connection_;
- DelegateWrapper* delegate_wrapper_;
- AndroidWebSocket::Delegate* delegate_;
- base::WeakPtrFactory<AndroidWebSocketImpl> weak_factory_;
- DISALLOW_COPY_AND_ASSIGN(AndroidWebSocketImpl);
+ scoped_ptr<net::StreamSocket> socket_;
+ Delegate* delegate_;
+ std::string response_buffer_;
+ std::string request_buffer_;
};
-AndroidWebSocketImpl::AndroidWebSocketImpl(
+WebSocketImpl::WebSocketImpl(
scoped_refptr<base::MessageLoopProxy> device_message_loop,
scoped_refptr<Device> device,
const std::string& socket_name,
const std::string& url,
- AndroidWebSocket::Delegate* delegate)
+ Delegate* delegate)
: device_message_loop_(device_message_loop),
device_(device),
socket_name_(socket_name),
url_(url),
- delegate_(delegate),
- weak_factory_(this) {
+ delegate_(delegate) {
+}
+
+void WebSocketImpl::Connect() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(delegate_);
device_->HttpUpgrade(
- socket_name_, url_,
- base::Bind(&AndroidWebSocketImpl::Connected, weak_factory_.GetWeakPtr()));
+ socket_name_, url_, base::Bind(&WebSocketImpl::Connected, this));
}
-void AndroidWebSocketImpl::SendFrame(const std::string& message) {
+void WebSocketImpl::Disconnect() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
device_message_loop_->PostTask(
FROM_HERE,
- base::Bind(&WebSocketImpl::SendFrame,
- base::Unretained(connection_), message));
+ base::Bind(&WebSocketImpl::DisconnectOnHandlerThread, this, false));
}
void WebSocketImpl::SendFrame(const std::string& message) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (!socket_)
- return;
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ device_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&WebSocketImpl::SendFrameOnHandlerThread, this, message));
+}
+
+void WebSocketImpl::ClearDelegate() {
+ delegate_ = NULL;
+}
+
+void WebSocketImpl::SendFrameOnHandlerThread(const std::string& message) {
+ DCHECK_EQ(device_message_loop_, base::MessageLoopProxy::current());
int mask = base::RandInt(0, 0x7FFFFFFF);
std::string encoded_frame = WebSocket::EncodeFrameHybi17(message, mask);
request_buffer_ += encoded_frame;
SendPendingRequests(0);
}
-AndroidWebSocketImpl::~AndroidWebSocketImpl() {
+WebSocketImpl::~WebSocketImpl() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- device_message_loop_->DeleteSoon(FROM_HERE, connection_);
- device_message_loop_->DeleteSoon(FROM_HERE, delegate_wrapper_);
-}
-
-WebSocketImpl::WebSocketImpl(Delegate* delegate,
- scoped_ptr<net::StreamSocket> socket)
- : delegate_(delegate),
- socket_(socket.Pass()) {
- thread_checker_.DetachFromThread();
}
-void AndroidWebSocketImpl::Connected(int result,
- scoped_ptr<net::StreamSocket> socket) {
+void WebSocketImpl::Connected(int result, net::StreamSocket* socket) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (result != net::OK || socket == NULL) {
- OnSocketClosed();
+ OnSocketClosed(true);
return;
}
- delegate_wrapper_ = new DelegateWrapper(weak_factory_.GetWeakPtr(),
- base::MessageLoopProxy::current());
- connection_ = new WebSocketImpl(delegate_wrapper_, socket.Pass());
+ socket_.reset(socket);
device_message_loop_->PostTask(
FROM_HERE,
- base::Bind(&WebSocketImpl::StartListening,
- base::Unretained(connection_)));
+ base::Bind(&WebSocketImpl::StartListeningOnHandlerThread, this));
OnSocketOpened();
}
-void WebSocketImpl::StartListening() {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(socket_);
+void WebSocketImpl::StartListeningOnHandlerThread() {
+ DCHECK_EQ(device_message_loop_, base::MessageLoopProxy::current());
scoped_refptr<net::IOBuffer> response_buffer =
new net::IOBuffer(kBufferSize);
int result = socket_->Read(
response_buffer.get(),
kBufferSize,
- base::Bind(&WebSocketImpl::OnBytesRead,
- base::Unretained(this), response_buffer));
+ base::Bind(&WebSocketImpl::OnBytesRead, this, response_buffer));
if (result != net::ERR_IO_PENDING)
OnBytesRead(response_buffer, result);
}
-void WebSocketImpl::OnBytesRead(scoped_refptr<net::IOBuffer> response_buffer,
- int result) {
- DCHECK(thread_checker_.CalledOnValidThread());
+void WebSocketImpl::OnBytesRead(
+ scoped_refptr<net::IOBuffer> response_buffer, int result) {
+ DCHECK_EQ(device_message_loop_, base::MessageLoopProxy::current());
+ if (!socket_)
+ return;
+
if (result <= 0) {
- Disconnect();
+ DisconnectOnHandlerThread(true);
return;
}
while (parse_result == WebSocket::FRAME_OK) {
response_buffer_ = response_buffer_.substr(bytes_consumed);
- delegate_->OnFrameRead(output);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&WebSocketImpl::OnFrameRead, this, output));
parse_result = WebSocket::DecodeFrameHybi17(
response_buffer_, false, &bytes_consumed, &output);
}
if (parse_result == WebSocket::FRAME_ERROR ||
parse_result == WebSocket::FRAME_CLOSE) {
- Disconnect();
+ DisconnectOnHandlerThread(true);
return;
}
result = socket_->Read(
response_buffer.get(),
kBufferSize,
- base::Bind(&WebSocketImpl::OnBytesRead,
- base::Unretained(this), response_buffer));
+ base::Bind(&WebSocketImpl::OnBytesRead, this, response_buffer));
if (result != net::ERR_IO_PENDING)
OnBytesRead(response_buffer, result);
}
void WebSocketImpl::SendPendingRequests(int result) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(device_message_loop_, base::MessageLoopProxy::current());
+ if (!socket_)
+ return;
if (result < 0) {
- Disconnect();
+ DisconnectOnHandlerThread(true);
return;
}
request_buffer_ = request_buffer_.substr(result);
new net::StringIOBuffer(request_buffer_);
result = socket_->Write(buffer.get(), buffer->size(),
base::Bind(&WebSocketImpl::SendPendingRequests,
- base::Unretained(this)));
+ this));
if (result != net::ERR_IO_PENDING)
SendPendingRequests(result);
}
-void WebSocketImpl::Disconnect() {
- DCHECK(thread_checker_.CalledOnValidThread());
- socket_.reset();
- delegate_->OnSocketClosed();
+void WebSocketImpl::DisconnectOnHandlerThread(bool closed_by_device) {
+ DCHECK_EQ(device_message_loop_, base::MessageLoopProxy::current());
+ if (!socket_)
+ return;
+ // Wipe out socket_ first since Disconnect can re-enter this method.
+ scoped_ptr<net::StreamSocket> socket(socket_.release());
+ socket->Disconnect();
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&WebSocketImpl::OnSocketClosed, this, closed_by_device));
}
-void AndroidWebSocketImpl::OnSocketOpened() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- delegate_->OnSocketOpened();
+void WebSocketImpl::OnSocketOpened() {
+ if (delegate_)
+ delegate_->OnSocketOpened();
}
-void AndroidWebSocketImpl::OnFrameRead(const std::string& message) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- delegate_->OnFrameRead(message);
+void WebSocketImpl::OnFrameRead(const std::string& message) {
+ if (delegate_)
+ delegate_->OnFrameRead(message);
}
-void AndroidWebSocketImpl::OnSocketClosed() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- delegate_->OnSocketClosed();
+void WebSocketImpl::OnSocketClosed(bool closed_by_device) {
+ if (delegate_)
+ delegate_->OnSocketClosed(closed_by_device);
}
} // namespace
-AndroidDeviceManager::AndroidWebSocket*
+scoped_refptr<AndroidDeviceManager::AndroidWebSocket>
AndroidDeviceManager::Device::CreateWebSocket(
const std::string& socket,
const std::string& url,
AndroidDeviceManager::AndroidWebSocket::Delegate* delegate) {
- return new AndroidWebSocketImpl(
- device_message_loop_, this, socket, url, delegate);
+ return new WebSocketImpl(device_message_loop_, this, socket, url, delegate);
}
private:
virtual void OnSocketOpened() OVERRIDE;
virtual void OnFrameRead(const std::string& message) OVERRIDE;
- virtual void OnSocketClosed() OVERRIDE;
- virtual ~ProtocolCommand();
+ virtual void OnSocketClosed(bool closed_by_device) OVERRIDE;
const std::string command_;
const base::Closure callback_;
- scoped_ptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_;
+ scoped_refptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_;
DISALLOW_COPY_AND_ASSIGN(ProtocolCommand);
};
const std::string& command,
const base::Closure callback)
: command_(command),
- callback_(callback),
- web_socket_(browser->CreateWebSocket(debug_url, this)) {
+ callback_(callback){
+ web_socket_ = browser->CreateWebSocket(debug_url, this);
+ web_socket_->Connect();
}
void ProtocolCommand::OnSocketOpened() {
}
void ProtocolCommand::OnFrameRead(const std::string& message) {
- delete this;
-}
-
-void ProtocolCommand::OnSocketClosed() {
- delete this;
+ web_socket_->Disconnect();
}
-ProtocolCommand::~ProtocolCommand() {
- if (!callback_.is_null())
+void ProtocolCommand::OnSocketClosed(bool closed_by_device) {
+ if (!callback_.is_null()) {
callback_.Run();
+ }
+ delete this;
}
} // namespace
const std::string& message) OVERRIDE;
virtual void OnSocketOpened() OVERRIDE;
virtual void OnFrameRead(const std::string& message) OVERRIDE;
- virtual void OnSocketClosed() OVERRIDE;
+ virtual void OnSocketClosed(bool closed_by_device) OVERRIDE;
const std::string id_;
- scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser_;
- const std::string debug_url_;
bool socket_opened_;
+ bool detached_;
bool is_web_view_;
std::vector<std::string> pending_messages_;
- scoped_ptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_;
+ scoped_refptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_;
content::DevToolsAgentHost* agent_host_;
content::DevToolsExternalAgentProxy* proxy_;
DISALLOW_COPY_AND_ASSIGN(AgentHostDelegate);
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
const std::string& debug_url)
: id_(id),
- browser_(browser),
- debug_url_(debug_url),
socket_opened_(false),
+ detached_(false),
is_web_view_(browser->IsWebView()),
+ web_socket_(browser->CreateWebSocket(debug_url, this)),
agent_host_(NULL),
proxy_(NULL) {
g_host_delegates.Get()[id] = this;
AgentHostDelegate::~AgentHostDelegate() {
g_host_delegates.Get().erase(id_);
+ web_socket_->ClearDelegate();
}
void AgentHostDelegate::Attach(content::DevToolsExternalAgentProxy* proxy) {
proxy_ = proxy;
content::RecordAction(base::UserMetricsAction(is_web_view_ ?
"DevTools_InspectAndroidWebView" : "DevTools_InspectAndroidPage"));
- web_socket_.reset(browser_->CreateWebSocket(debug_url_, this));
+ web_socket_->Connect();
}
void AgentHostDelegate::Detach() {
- web_socket_.reset();
+ detached_ = true;
+ if (socket_opened_)
+ web_socket_->Disconnect();
}
void AgentHostDelegate::SendMessageToBackend(const std::string& message) {
}
void AgentHostDelegate::OnSocketOpened() {
+ if (detached_) {
+ web_socket_->Disconnect();
+ return;
+ }
+
socket_opened_ = true;
for (std::vector<std::string>::iterator it = pending_messages_.begin();
it != pending_messages_.end(); ++it) {
proxy_->DispatchOnClientHost(message);
}
-void AgentHostDelegate::OnSocketClosed() {
- if (proxy_)
+void AgentHostDelegate::OnSocketClosed(bool closed_by_device) {
+ if (proxy_ && closed_by_device)
proxy_->ConnectionClosed();
}
"adb:" + device_->serial() + ":" + socket_, this, kBrowserTargetSocket);
}
-DevToolsAndroidBridge::AndroidWebSocket*
+scoped_refptr<DevToolsAndroidBridge::AndroidWebSocket>
DevToolsAndroidBridge::RemoteBrowser::CreateWebSocket(
const std::string& url,
DevToolsAndroidBridge::AndroidWebSocket::Delegate* delegate) {
scoped_refptr<content::DevToolsAgentHost> GetAgentHost();
- AndroidWebSocket* CreateWebSocket(
+ scoped_refptr<AndroidWebSocket> CreateWebSocket(
const std::string& url,
DevToolsAndroidBridge::AndroidWebSocket::Delegate* delegate);
int port,
const CounterCallback& callback,
int result,
- scoped_ptr<net::StreamSocket> socket) {
+ net::StreamSocket* socket) {
if (result < 0)
return;
SocketTunnel* tunnel = new SocketTunnel(callback);
- tunnel->Start(socket.Pass(), host, port);
+ tunnel->Start(socket, host, port);
}
private:
callback_.Run(1);
}
- void Start(scoped_ptr<net::StreamSocket> socket,
- const std::string& host, int port) {
- remote_socket_.swap(socket);
+ void Start(net::StreamSocket* socket, const std::string& host, int port) {
+ remote_socket_.reset(socket);
host_resolver_ = net::HostResolver::CreateDefaultResolver(NULL);
net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port));
} // namespace
class PortForwardingController::Connection
- : public DevToolsAndroidBridge::AndroidWebSocket::Delegate {
+ : public DevToolsAndroidBridge::AndroidWebSocket::Delegate,
+ public base::RefCountedThreadSafe<
+ Connection,
+ content::BrowserThread::DeleteOnUIThread> {
public:
Connection(Registry* registry,
scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device,
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
const ForwardingMap& forwarding_map);
- virtual ~Connection();
const PortStatusMap& GetPortStatusMap();
content::BrowserThread::UI>;
friend class base::DeleteHelper<Connection>;
+ virtual ~Connection();
typedef std::map<int, std::string> ForwardingMap;
void ProcessBindResponse(int port, PortStatus status);
void ProcessUnbindResponse(int port, PortStatus status);
- static void UpdateSocketCountOnHandlerThread(
- base::WeakPtr<Connection> weak_connection, int port, int increment);
+ void UpdateSocketCountOnHandlerThread(int port, int increment);
void UpdateSocketCount(int port, int increment);
// DevToolsAndroidBridge::AndroidWebSocket::Delegate implementation:
virtual void OnSocketOpened() OVERRIDE;
virtual void OnFrameRead(const std::string& message) OVERRIDE;
- virtual void OnSocketClosed() OVERRIDE;
+ virtual void OnSocketClosed(bool closed_by_device) OVERRIDE;
PortForwardingController::Registry* registry_;
scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device_;
scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser_;
- scoped_ptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_;
+ scoped_refptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_;
int command_id_;
bool connected_;
ForwardingMap forwarding_map_;
CommandCallbackMap pending_responses_;
PortStatusMap port_status_;
- base::WeakPtrFactory<Connection> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Connection);
};
browser_(browser),
command_id_(0),
connected_(false),
- forwarding_map_(forwarding_map),
- weak_factory_(this) {
+ forwarding_map_(forwarding_map) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
(*registry_)[device_->serial()] = this;
- web_socket_.reset(
- browser->CreateWebSocket(kDevToolsRemoteBrowserTarget, this));
+ web_socket_ = browser->CreateWebSocket(kDevToolsRemoteBrowserTarget, this);
+ web_socket_->Connect();
+ AddRef(); // Balanced in OnSocketClosed();
+}
+
+void PortForwardingController::Connection::Shutdown() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ registry_ = NULL;
+ // This will have no effect if the socket is not connected yet.
+ web_socket_->Disconnect();
}
PortForwardingController::Connection::~Connection() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(registry_->find(device_->serial()) != registry_->end());
- registry_->erase(device_->serial());
+ if (registry_) {
+ DCHECK(registry_->find(device_->serial()) != registry_->end());
+ registry_->erase(device_->serial());
+ }
}
void PortForwardingController::Connection::UpdateForwardingMap(
port_status_.erase(it);
}
-// static
void PortForwardingController::Connection::UpdateSocketCountOnHandlerThread(
- base::WeakPtr<Connection> weak_connection, int port, int increment) {
+ int port, int increment) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- base::Bind(&Connection::UpdateSocketCount,
- weak_connection, port, increment));
+ base::Bind(&Connection::UpdateSocketCount, this, port, increment));
}
void PortForwardingController::Connection::UpdateSocketCount(
void PortForwardingController::Connection::OnSocketOpened() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!registry_) {
+ // Socket was created after Shutdown was called. Disconnect immediately.
+ web_socket_->Disconnect();
+ return;
+ }
connected_ = true;
SerializeChanges(kTetheringBind, ForwardingMap(), forwarding_map_);
}
-void PortForwardingController::Connection::OnSocketClosed() {
- delete this;
+void PortForwardingController::Connection::OnSocketClosed(
+ bool closed_by_device) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ Release(); // Balanced in the constructor.
}
void PortForwardingController::Connection::OnFrameRead(
std::string destination_host = tokens[0];
SocketTunnel::CounterCallback callback =
- base::Bind(&Connection::UpdateSocketCountOnHandlerThread,
- weak_factory_.GetWeakPtr(), port);
+ base::Bind(&Connection::UpdateSocketCountOnHandlerThread, this, port);
device_->OpenSocket(
connection_id.c_str(),
UpdateConnections();
} else {
StopListening();
- STLDeleteValues(®istry_);
+ ShutdownConnections();
NotifyListeners(DevicesStatus());
}
}
it->second->UpdateForwardingMap(forwarding_map_);
}
+void PortForwardingController::ShutdownConnections() {
+ for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it)
+ it->second->Shutdown();
+ registry_.clear();
+}
+
void PortForwardingController::NotifyListeners(
const DevicesStatus& status) const {
Listeners copy(listeners_); // Iterate over copy.
void StopListening();
void UpdateConnections();
+ void ShutdownConnections();
void NotifyListeners(const DevicesStatus& status) const;
static void RunSocketCallback(
const AndroidDeviceManager::SocketCallback& callback,
- scoped_ptr<net::StreamSocket> socket,
+ net::StreamSocket* socket,
int result) {
- callback.Run(result, socket.Pass());
+ callback.Run(result, socket);
}
} // namespace
base::StringToInt(socket_name, &port);
net::AddressList address_list =
net::AddressList::CreateFromIPAddress(ip_number, port);
- scoped_ptr<net::StreamSocket> socket(new net::TCPClientSocket(
- address_list, NULL, net::NetLog::Source()));
- socket->Connect(
- base::Bind(&RunSocketCallback, callback, base::Passed(&socket)));
+ net::TCPClientSocket* socket = new net::TCPClientSocket(
+ address_list, NULL, net::NetLog::Source());
+ socket->Connect(base::Bind(&RunSocketCallback, callback, socket));
}
const int kBufferSize = 16 * 1024;
void OnOpenSocket(const UsbDeviceProvider::SocketCallback& callback,
- net::StreamSocket* socket_raw,
+ net::StreamSocket* socket,
int result) {
- scoped_ptr<net::StreamSocket> socket(socket_raw);
- if (result != net::OK)
- socket.reset();
- callback.Run(result, socket.Pass());
+ callback.Run(result, result == net::OK ? socket : NULL);
}
void OnRead(net::StreamSocket* socket,
callback.Run(net::ERR_CONNECTION_FAILED, std::string());
return;
}
- int result = socket->Connect(
- base::Bind(&OpenedForCommand, callback, socket));
+ int result = socket->Connect(base::Bind(&OpenedForCommand, callback, socket));
if (result != net::ERR_IO_PENDING)
callback.Run(result, std::string());
}
const SocketCallback& callback) {
UsbDeviceMap::iterator it = device_map_.find(serial);
if (it == device_map_.end()) {
- callback.Run(net::ERR_CONNECTION_FAILED,
- make_scoped_ptr<net::StreamSocket>(NULL));
+ callback.Run(net::ERR_CONNECTION_FAILED, NULL);
return;
}
std::string socket_name =
base::StringPrintf(kLocalAbstractCommand, name.c_str());
net::StreamSocket* socket = it->second->CreateSocket(socket_name);
if (!socket) {
- callback.Run(net::ERR_CONNECTION_FAILED,
- make_scoped_ptr<net::StreamSocket>(NULL));
+ callback.Run(net::ERR_CONNECTION_FAILED, NULL);
return;
}
int result = socket->Connect(base::Bind(&OnOpenSocket, callback, socket));
if (result != net::ERR_IO_PENDING)
- callback.Run(result, make_scoped_ptr<net::StreamSocket>(NULL));
+ callback.Run(result, NULL);
}
void UsbDeviceProvider::ReleaseDevice(const std::string& serial) {
#include "base/command_line.h"
#include "base/metrics/field_trial.h"
+#include "base/prefs/pref_service.h"
+#include "chrome/browser/browser_process.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
#include "components/domain_reliability/service.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "content/public/browser/browser_context.h"
if (!IsDomainReliabilityMonitoringEnabled())
return NULL;
+ if (!g_browser_process->local_state()->GetBoolean(
+ prefs::kMetricsReportingEnabled)) {
+ return NULL;
+ }
+
return DomainReliabilityService::Create(
kDomainReliabilityUploadReporterString);
}
if (options->alpha_enabled.get()) {
const char* whitelist[] = {
+#if defined(OS_CHROMEOS)
+ "B58B99751225318C7EB8CF4688B5434661083E07", // http://crbug.com/410550
+#endif
"0F42756099D914A026DADFA182871C015735DD95", // http://crbug.com/323773
"2D22CDB6583FD0A13758AEBE8B15E45208B4E9A7",
"E7E2461CE072DF036CF9592740196159E2D7C089", // http://crbug.com/356200
results_ = bluetooth_socket::ListenUsingL2cap::Results::Create();
}
-BluetoothSocketConnectFunction::BluetoothSocketConnectFunction() {}
+BluetoothSocketAbstractConnectFunction::
+ BluetoothSocketAbstractConnectFunction() {}
-BluetoothSocketConnectFunction::~BluetoothSocketConnectFunction() {}
+BluetoothSocketAbstractConnectFunction::
+ ~BluetoothSocketAbstractConnectFunction() {}
-bool BluetoothSocketConnectFunction::Prepare() {
+bool BluetoothSocketAbstractConnectFunction::Prepare() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
params_ = bluetooth_socket::Connect::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_.get());
return socket_event_dispatcher_ != NULL;
}
-void BluetoothSocketConnectFunction::AsyncWorkStart() {
+void BluetoothSocketAbstractConnectFunction::AsyncWorkStart() {
DCHECK(BrowserThread::CurrentlyOn(work_thread_id()));
device::BluetoothAdapterFactory::GetAdapter(
- base::Bind(&BluetoothSocketConnectFunction::OnGetAdapter, this));
+ base::Bind(&BluetoothSocketAbstractConnectFunction::OnGetAdapter, this));
}
-void BluetoothSocketConnectFunction::OnGetAdapter(
+void BluetoothSocketAbstractConnectFunction::OnGetAdapter(
scoped_refptr<device::BluetoothAdapter> adapter) {
DCHECK(BrowserThread::CurrentlyOn(work_thread_id()));
BluetoothApiSocket* socket = GetSocket(params_->socket_id);
return;
}
- device->ConnectToService(
- uuid,
- base::Bind(&BluetoothSocketConnectFunction::OnConnect, this),
- base::Bind(&BluetoothSocketConnectFunction::OnConnectError, this));
+ ConnectToService(device, uuid);
}
-void BluetoothSocketConnectFunction::OnConnect(
+void BluetoothSocketAbstractConnectFunction::OnConnect(
scoped_refptr<device::BluetoothSocket> socket) {
DCHECK(BrowserThread::CurrentlyOn(work_thread_id()));
AsyncWorkCompleted();
}
-void BluetoothSocketConnectFunction::OnConnectError(
+void BluetoothSocketAbstractConnectFunction::OnConnectError(
const std::string& message) {
DCHECK(BrowserThread::CurrentlyOn(work_thread_id()));
error_ = message;
AsyncWorkCompleted();
}
+BluetoothSocketConnectFunction::BluetoothSocketConnectFunction() {}
+
+BluetoothSocketConnectFunction::~BluetoothSocketConnectFunction() {}
+
+void BluetoothSocketConnectFunction::ConnectToService(
+ device::BluetoothDevice* device,
+ const device::BluetoothUUID& uuid) {
+ device->ConnectToService(
+ uuid,
+ base::Bind(&BluetoothSocketConnectFunction::OnConnect, this),
+ base::Bind(&BluetoothSocketConnectFunction::OnConnectError, this));
+}
+
BluetoothSocketDisconnectFunction::BluetoothSocketDisconnectFunction() {}
BluetoothSocketDisconnectFunction::~BluetoothSocketDisconnectFunction() {}
scoped_ptr<bluetooth_socket::ListenUsingL2cap::Params> params_;
};
-class BluetoothSocketConnectFunction : public BluetoothSocketAsyncApiFunction {
+class BluetoothSocketAbstractConnectFunction :
+ public BluetoothSocketAsyncApiFunction {
public:
- DECLARE_EXTENSION_FUNCTION("bluetoothSocket.connect",
- BLUETOOTHSOCKET_CONNECT);
-
- BluetoothSocketConnectFunction();
+ BluetoothSocketAbstractConnectFunction();
protected:
- virtual ~BluetoothSocketConnectFunction();
+ virtual ~BluetoothSocketAbstractConnectFunction();
// BluetoothSocketAsyncApiFunction:
virtual bool Prepare() OVERRIDE;
virtual void AsyncWorkStart() OVERRIDE;
- private:
- virtual void OnGetAdapter(scoped_refptr<device::BluetoothAdapter> adapter);
+ // Subclasses should implement this method to connect to the service
+ // registered with |uuid| on the |device|.
+ virtual void ConnectToService(device::BluetoothDevice* device,
+ const device::BluetoothUUID& uuid) = 0;
+
virtual void OnConnect(scoped_refptr<device::BluetoothSocket> socket);
virtual void OnConnectError(const std::string& message);
+ private:
+ virtual void OnGetAdapter(scoped_refptr<device::BluetoothAdapter> adapter);
+
scoped_ptr<bluetooth_socket::Connect::Params> params_;
BluetoothSocketEventDispatcher* socket_event_dispatcher_;
};
+class BluetoothSocketConnectFunction :
+ public BluetoothSocketAbstractConnectFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("bluetoothSocket.connect",
+ BLUETOOTHSOCKET_CONNECT);
+
+ BluetoothSocketConnectFunction();
+
+ protected:
+ virtual ~BluetoothSocketConnectFunction();
+
+ // BluetoothSocketAbstractConnectFunction:
+ virtual void ConnectToService(device::BluetoothDevice* device,
+ const device::BluetoothUUID& uuid) OVERRIDE;
+};
+
class BluetoothSocketDisconnectFunction
: public BluetoothSocketAsyncApiFunction {
public:
return EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE;
case easy_unlock_private::STATE_PHONE_NOT_NEARBY:
return EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY;
+ case easy_unlock_private::STATE_PHONE_UNSUPPORTED:
+ return EasyUnlockScreenlockStateHandler::STATE_PHONE_UNSUPPORTED;
case easy_unlock_private::STATE_AUTHENTICATED:
return EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED;
default:
}
}
+EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
+ EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction() {}
+
+EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
+ ~EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction() {}
+
+void EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
+ ConnectToService(device::BluetoothDevice* device,
+ const device::BluetoothUUID& uuid) {
+ easy_unlock::ConnectToBluetoothServiceInsecurely(
+ device,
+ uuid,
+ base::Bind(&EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
+ OnConnect,
+ this),
+ base::Bind(&EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction::
+ OnConnectError,
+ this));
+}
+
EasyUnlockPrivateUpdateScreenlockStateFunction::
EasyUnlockPrivateUpdateScreenlockStateFunction() {}
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/extensions/api/bluetooth_socket/bluetooth_socket_api.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/extension_function.h"
EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction);
};
+class EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction
+ : public BluetoothSocketAbstractConnectFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION(
+ "easyUnlockPrivate.connectToBluetoothServiceInsecurely",
+ EASYUNLOCKPRIVATE_CONNECTTOBLUETOOTHSERVICEINSECURELY)
+ EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction();
+
+ private:
+ virtual ~EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction();
+
+ // BluetoothSocketAbstractConnectFunction:
+ virtual void ConnectToService(device::BluetoothDevice* device,
+ const device::BluetoothUUID& uuid) OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(
+ EasyUnlockPrivateConnectToBluetoothServiceInsecurelyFunction);
+};
+
class EasyUnlockPrivateUpdateScreenlockStateFunction
: public SyncExtensionFunction {
public:
#include "base/callback.h"
+using device::BluetoothDevice;
+
namespace extensions {
namespace api {
namespace easy_unlock {
result.error_message = kApiUnavailable;
callback.Run(result);
}
+
+void ConnectToBluetoothServiceInsecurely(
+ device::BluetoothDevice* device,
+ const device::BluetoothUUID& uuid,
+ const BluetoothDevice::ConnectToServiceCallback& callback,
+ const BluetoothDevice::ConnectToServiceErrorCallback& error_callback) {
+ error_callback.Run(kApiUnavailable);
+}
#endif // !defined(OS_CHROMEOS)
} // namespace easy_unlock
#include <string>
#include "base/callback_forward.h"
+#include "device/bluetooth/bluetooth_device.h"
+
+namespace device {
+class BluetoothUUID;
+}
namespace extensions {
namespace api {
void SeekBluetoothDeviceByAddress(const std::string& device_address,
const SeekDeviceCallback& callback);
+void ConnectToBluetoothServiceInsecurely(
+ device::BluetoothDevice* device,
+ const device::BluetoothUUID& uuid,
+ const device::BluetoothDevice::ConnectToServiceCallback& callback,
+ const device::BluetoothDevice::ConnectToServiceErrorCallback&
+ error_callback);
+
} // namespace easy_unlock
} // namespace api
} // namespace extensions
#include "base/time/time.h"
#include "content/public/browser/browser_thread.h"
#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_device_chromeos.h"
#include "net/socket/socket_descriptor.h"
namespace extensions {
callback);
}
+void ConnectToBluetoothServiceInsecurely(
+ device::BluetoothDevice* device,
+ const device::BluetoothUUID& uuid,
+ const BluetoothDevice::ConnectToServiceCallback& callback,
+ const BluetoothDevice::ConnectToServiceErrorCallback& error_callback) {
+ static_cast<chromeos::BluetoothDeviceChromeOS*>(device)
+ ->ConnectToServiceInsecurely(uuid, callback, error_callback);
+}
+
} // namespace easy_unlock
} // namespace api
} // namespace extensions
}
private:
- scoped_ptr<chromeos::EasyUnlockClient> dbus_client_;
+ chromeos::EasyUnlockClient* dbus_client_;
DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivateCryptoDelegateChromeOS);
};
const char kAPINotAvailableForUser[] =
"The API is not available for this user.";
const int kObfuscatedGaiaIdTimeoutInDays = 30;
-}
+const char kDeprecationMessage[] =
+ "The chrome.pushMessaging API is deprecated. Use chrome.gcm API instead.";
+} // namespace
namespace glue = api::push_messaging;
PushMessagingGetChannelIdFunction::~PushMessagingGetChannelIdFunction() {}
bool PushMessagingGetChannelIdFunction::RunAsync() {
+ // Issue a deprecation message.
+ WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_WARNING, kDeprecationMessage);
+
// Fetch the function arguments.
scoped_ptr<glue::GetChannelId::Params> params(
glue::GetChannelId::Params::Create(*args_));
// locking.
NOTREACHED();
return screenlock::AUTH_TYPE_NONE;
+ case ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD:
+ return screenlock::AUTH_TYPE_OFFLINEPASSWORD;
}
NOTREACHED();
return screenlock::AUTH_TYPE_OFFLINEPASSWORD;
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_switches.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_message_filter.h"
#include "extensions/browser/extension_registry.h"
if (!process)
return;
DCHECK(profile);
- if (ProcessMap::Get(profile)->Contains(process->GetID()))
+ if (ProcessMap::Get(profile)->Contains(process->GetID())) {
command_line->AppendSwitch(switches::kExtensionProcess);
+#if defined(ENABLE_WEBRTC)
+ command_line->AppendSwitch(::switches::kEnableWebRtcHWH264Encoding);
+#endif
+ }
}
} // namespace extensions
if (!ManifestURL::UpdatesFromGallery(&extension))
update_url_data = delegate_->GetUpdateUrlData(extension.id());
- return AddExtensionData(extension.id(), *extension.version(),
+ std::string install_source;
+ bool force_update = delegate_->ShouldForceUpdate(extension.id(),
+ &install_source);
+ return AddExtensionData(extension.id(),
+ *extension.version(),
extension.GetType(),
ManifestURL::GetUpdateURL(&extension),
- update_url_data, request_id);
+ update_url_data,
+ request_id,
+ force_update,
+ install_source);
}
bool ExtensionDownloader::AddPendingExtension(const std::string& id,
Manifest::TYPE_UNKNOWN,
update_url,
std::string(),
- request_id);
+ request_id,
+ false,
+ std::string());
}
void ExtensionDownloader::StartAllPending(ExtensionCache* cache) {
version,
&ping_data,
std::string(),
- kDefaultInstallSource);
+ kDefaultInstallSource,
+ false);
StartUpdateCheck(blacklist_fetch.Pass());
}
identity_provider_.swap(identity_provider);
}
-bool ExtensionDownloader::AddExtensionData(const std::string& id,
- const Version& version,
- Manifest::Type extension_type,
- const GURL& extension_update_url,
- const std::string& update_url_data,
- int request_id) {
+bool ExtensionDownloader::AddExtensionData(
+ const std::string& id,
+ const Version& version,
+ Manifest::Type extension_type,
+ const GURL& extension_update_url,
+ const std::string& update_url_data,
+ int request_id,
+ bool force_update,
+ const std::string& install_source_override) {
GURL update_url(extension_update_url);
// Skip extensions with non-empty invalid update URLs.
if (!update_url.is_empty() && !update_url.is_valid()) {
std::string install_source = i == 0 ?
kDefaultInstallSource : kNotFromWebstoreInstallSource;
+ if (!install_source_override.empty()) {
+ install_source = install_source_override;
+ }
ManifestFetchData::PingData ping_data;
ManifestFetchData::PingData* optional_ping_data = NULL;
ManifestFetchData* existing_fetch = existing_iter->second.back().get();
if (existing_fetch->AddExtension(id, version.GetString(),
optional_ping_data, update_url_data,
- install_source)) {
+ install_source,
+ force_update)) {
added = true;
}
}
added = fetch->AddExtension(id, version.GetString(),
optional_ping_data,
update_url_data,
- install_source);
+ install_source,
+ force_update);
DCHECK(added);
}
}
VLOG(2) << id << " is at '" << version << "'";
- Version existing_version(version);
- Version update_version(update->version);
-
- if (!update_version.IsValid() ||
- update_version.CompareTo(existing_version) <= 0) {
- continue;
+ // We should skip the version check if update was forced.
+ if (!fetch_data.DidForceUpdate(id)) {
+ Version existing_version(version);
+ Version update_version(update->version);
+ if (!update_version.IsValid() ||
+ update_version.CompareTo(existing_version) <= 0) {
+ continue;
+ }
}
}
Manifest::Type extension_type,
const GURL& extension_update_url,
const std::string& update_url_data,
- int request_id);
+ int request_id,
+ bool force_update,
+ const std::string& install_source_override);
// Adds all recorded stats taken so far to histogram counts.
void ReportStats() const;
return std::string();
}
+bool ExtensionDownloaderDelegate::ShouldForceUpdate(
+ const std::string& id,
+ std::string* source) {
+ return false;
+}
+
} // namespace extensions
// that extension is not installed.
virtual bool GetExtensionExistingVersion(const std::string& id,
std::string* version) = 0;
+
+ // Determines if a given extension should be forced to update and (if so)
+ // what the source of this forcing is (i.e. what string will be passed
+ // in |installsource| as part of the update query parameters). The default
+ // implementation always returns |false|.
+ virtual bool ShouldForceUpdate(const std::string& id,
+ std::string* source);
};
} // namespace extensions
#include <set>
#include "base/bind.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/extensions/pending_extension_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
+#include "components/omaha_query_params/omaha_query_params.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
using base::Time;
using base::TimeDelta;
using content::BrowserThread;
+using extensions::Extension;
+using extensions::ExtensionSet;
+using omaha_query_params::OmahaQueryParams;
typedef extensions::ExtensionDownloaderDelegate::Error Error;
typedef extensions::ExtensionDownloaderDelegate::PingResult PingResult;
// checks.
const int kMinUpdateThrottleTime = 5;
+// The installsource query parameter to use when forcing updates due to NaCl
+// arch mismatch.
+const char kWrongMultiCrxInstallSource[] = "wrong_multi_crx";
+
// When we've computed a days value, we want to make sure we don't send a
// negative value (due to the system clock being set backwards, etc.), since -1
// is a special sentinel value that means "never pinged", and other negative
return SanitizeDays((Time::Now() - last_active_ping_day).InDays());
}
+void RespondWithForcedUpdates(
+ const base::Callback<void(const std::set<std::string>&)>& callback,
+ scoped_ptr<std::set<std::string> > forced_updates) {
+ callback.Run(*forced_updates.get());
+}
+
+void DetermineForcedUpdatesOnBlockingPool(
+ scoped_ptr<std::vector<scoped_refptr<const Extension> > > extensions,
+ const base::Callback<void(const std::set<std::string>&)>& callback) {
+ scoped_ptr<std::set<std::string> > forced_updates(
+ new std::set<std::string>());
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
+ for (std::vector<scoped_refptr<const Extension> >::const_iterator iter =
+ extensions->begin();
+ iter != extensions->end();
+ ++iter) {
+ scoped_refptr<const Extension> extension = *iter;
+ base::FilePath platform_specific_path = extension->path().Append(
+ extensions::kPlatformSpecificFolder);
+ if (base::PathExists(platform_specific_path)) {
+ bool force = true;
+ base::FileEnumerator all_archs(platform_specific_path,
+ false,
+ base::FileEnumerator::DIRECTORIES);
+ base::FilePath arch;
+ while (!(arch = all_archs.Next()).empty()) {
+ std::string arch_name = arch.BaseName().AsUTF8Unsafe();
+ std::replace(arch_name.begin(), arch_name.end(), '_', '-');
+ if (arch_name == OmahaQueryParams::GetNaclArch())
+ force = false;
+ }
+
+ if (force)
+ forced_updates->insert(extension->id());
+ }
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&RespondWithForcedUpdates,
+ callback,
+ base::Passed(&forced_updates)));
+}
+
+void CollectExtensionsFromSet(
+ const ExtensionSet& extensions,
+ std::vector<scoped_refptr<const Extension> >* paths) {
+ std::copy(extensions.begin(), extensions.end(), std::back_inserter(*paths));
+}
+
+void DetermineForcedUpdates(
+ content::BrowserContext* browser_context,
+ const base::Callback<void(const std::set<std::string>&)>& callback) {
+ scoped_ptr<std::vector<scoped_refptr<const Extension> > > extensions(
+ new std::vector<scoped_refptr<const Extension> >());
+ const extensions::ExtensionRegistry* registry =
+ extensions::ExtensionRegistry::Get(browser_context);
+ scoped_ptr<ExtensionSet> installed_extensions =
+ registry->GenerateInstalledExtensionsSet();
+ CollectExtensionsFromSet(*installed_extensions.get(), extensions.get());
+ BrowserThread::PostBlockingPoolTask(
+ FROM_HERE,
+ base::Bind(&DetermineForcedUpdatesOnBlockingPool,
+ base::Passed(&extensions),
+ callback));
+}
+
} // namespace
namespace extensions {
}
void ExtensionUpdater::CheckNow(const CheckParams& params) {
+ DetermineForcedUpdates(
+ profile_,
+ base::Bind(&ExtensionUpdater::OnForcedUpdatesDetermined,
+ weak_ptr_factory_.GetWeakPtr(),
+ params));
+}
+
+void ExtensionUpdater::OnForcedUpdatesDetermined(
+ const CheckParams& params,
+ const std::set<std::string>& forced_updates) {
int request_id = next_request_id_++;
VLOG(2) << "Starting update check " << request_id;
EnsureDownloaderCreated();
+ forced_updates_ = forced_updates;
+
// Add fetch records for extensions that should be fetched by an update URL.
// These extensions are not yet installed. They come from group policy
// and external install sources.
return true;
}
+bool ExtensionUpdater::ShouldForceUpdate(
+ const std::string& extension_id,
+ std::string* source) {
+ bool force = forced_updates_.find(extension_id) != forced_updates_.end();
+ // Currently the only reason to force is a NaCl arch mismatch with the
+ // installed extension contents.
+ if (force) {
+ *source = kWrongMultiCrxInstallSource;
+ }
+ return force;
+}
+
void ExtensionUpdater::UpdatePingData(const std::string& id,
const PingResult& ping_result) {
DCHECK(alive_);
struct ThrottleInfo;
+ // Callback used to continue CheckNow after determining which extensions
+ // should be force-updated.
+ void OnForcedUpdatesDetermined(const CheckParams& params,
+ const std::set<std::string>& forced_updates);
+
// Ensure that we have a valid ExtensionDownloader instance referenced by
// |downloader|.
void EnsureDownloaderCreated();
Error error,
const PingResult& ping,
const std::set<int>& request_ids) OVERRIDE;
-
virtual void OnExtensionDownloadFinished(
const std::string& id,
const base::FilePath& path,
const std::string& version,
const PingResult& ping,
const std::set<int>& request_id) OVERRIDE;
-
- // Implementation of ExtensionRegistryObserver.
- virtual void OnExtensionWillBeInstalled(
- content::BrowserContext* browser_context,
- const Extension* extension,
- bool is_update,
- bool from_ephemeral,
- const std::string& old_name) OVERRIDE;
-
virtual bool GetPingDataForExtension(
const std::string& id,
ManifestFetchData::PingData* ping_data) OVERRIDE;
-
virtual std::string GetUpdateUrlData(const std::string& id) OVERRIDE;
-
virtual bool IsExtensionPending(const std::string& id) OVERRIDE;
-
virtual bool GetExtensionExistingVersion(const std::string& id,
std::string* version) OVERRIDE;
+ virtual bool ShouldForceUpdate(const std::string& extension_id,
+ std::string* source) OVERRIDE;
void UpdatePingData(const std::string& id, const PingResult& ping_result);
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
+ // Implementation of ExtensionRegistryObserver.
+ virtual void OnExtensionWillBeInstalled(
+ content::BrowserContext* browser_context,
+ const Extension* extension,
+ bool is_update,
+ bool from_ephemeral,
+ const std::string& old_name) OVERRIDE;
+
// Send a notification that update checks are starting.
void NotifyStarted();
// checks to prevent too many requests from being made.
std::map<std::string, ThrottleInfo> throttle_info_;
+ // Keeps track of extensions (by ID) whose update should be forced during the
+ // next update check.
+ std::set<std::string> forced_updates_;
+
DISALLOW_COPY_AND_ASSIGN(ExtensionUpdater);
};
EXPECT_TRUE(updater->timer_.IsRunning());
updater->timer_.Stop();
updater->TimerFired();
+ content::RunAllBlockingPoolTasksUntilIdle();
}
// Adds a Result with the given data to results.
// option to appear in the x= parameter.
ManifestFetchData fetch_data(GURL("http://localhost/foo"), 0);
fetch_data.AddExtension(
- id, version, &kNeverPingedData, std::string(), std::string());
+ id, version, &kNeverPingedData, std::string(), std::string(), false);
std::map<std::string, std::string> params;
VerifyQueryAndExtractParameters(fetch_data.full_url().query(), ¶ms);
// option to appear in the x= parameter.
ManifestFetchData fetch_data(GURL("http://localhost/foo"), 0);
fetch_data.AddExtension(
- id, version, &kNeverPingedData, "bar", std::string());
+ id, version, &kNeverPingedData, "bar", std::string(), false);
std::map<std::string, std::string> params;
VerifyQueryAndExtractParameters(fetch_data.full_url().query(), ¶ms);
EXPECT_EQ(id, params["id"]);
// option to appear in the x= parameter.
ManifestFetchData fetch_data(GURL("http://localhost/foo"), 0);
fetch_data.AddExtension(
- id, version, &kNeverPingedData, "a=1&b=2&c", std::string());
+ id, version, &kNeverPingedData, "a=1&b=2&c", std::string(), false);
std::map<std::string, std::string> params;
VerifyQueryAndExtractParameters(fetch_data.full_url().query(), ¶ms);
EXPECT_EQ(id, params["id"]);
// Make sure that an installsource= appears in the x= parameter.
ManifestFetchData fetch_data(GURL("http://localhost/foo"), 0);
fetch_data.AddExtension(id, version, &kNeverPingedData,
- kEmptyUpdateUrlData, install_source);
+ kEmptyUpdateUrlData, install_source, false);
std::map<std::string, std::string> params;
VerifyQueryAndExtractParameters(fetch_data.full_url().query(), ¶ms);
EXPECT_EQ(id, params["id"]);
const std::string id1 = id_util::GenerateId("1");
const std::string id2 = id_util::GenerateId("2");
fetch_data.AddExtension(
- id1, "1.0.0.0", &kNeverPingedData, kEmptyUpdateUrlData, std::string());
+ id1, "1.0.0.0", &kNeverPingedData, kEmptyUpdateUrlData, std::string(),
+ false);
AddParseResult(id1, "1.1", "http://localhost/e1_1.1.crx", &updates);
fetch_data.AddExtension(
- id2, "2.0.0.0", &kNeverPingedData, kEmptyUpdateUrlData, std::string());
+ id2, "2.0.0.0", &kNeverPingedData, kEmptyUpdateUrlData, std::string(),
+ false);
AddParseResult(id2, "2.0.0.0", "http://localhost/e2_2.0.crx", &updates);
EXPECT_CALL(delegate, IsExtensionPending(_)).WillRepeatedly(Return(false));
"1.0.0.0",
&kNeverPingedData,
kEmptyUpdateUrlData,
- std::string());
+ std::string(),
+ false);
AddParseResult(*it, "1.1", "http://localhost/e1_1.1.crx", &updates);
}
scoped_ptr<ManifestFetchData> fetch4(new ManifestFetchData(kUpdateUrl, 0));
ManifestFetchData::PingData zeroDays(0, 0, true);
fetch1->AddExtension(
- "1111", "1.0", &zeroDays, kEmptyUpdateUrlData, std::string());
+ "1111", "1.0", &zeroDays, kEmptyUpdateUrlData, std::string(), false);
fetch2->AddExtension(
- "2222", "2.0", &zeroDays, kEmptyUpdateUrlData, std::string());
+ "2222", "2.0", &zeroDays, kEmptyUpdateUrlData, std::string(), false);
fetch3->AddExtension(
- "3333", "3.0", &zeroDays, kEmptyUpdateUrlData, std::string());
+ "3333", "3.0", &zeroDays, kEmptyUpdateUrlData, std::string(), false);
fetch4->AddExtension(
- "4444", "4.0", &zeroDays, kEmptyUpdateUrlData, std::string());
+ "4444", "4.0", &zeroDays, kEmptyUpdateUrlData, std::string(), false);
// This will start the first fetcher and queue the others. The next in queue
// is started as each fetcher receives its response. Note that the fetchers
scoped_ptr<ManifestFetchData> fetch(new ManifestFetchData(kUpdateUrl, 0));
ManifestFetchData::PingData zeroDays(0, 0, true);
fetch->AddExtension(
- "1111", "1.0", &zeroDays, kEmptyUpdateUrlData, std::string());
+ "1111", "1.0", &zeroDays, kEmptyUpdateUrlData, std::string(), false);
// This will start the first fetcher.
downloader.StartUpdateCheck(fetch.Pass());
// should not retry.
fetch.reset(new ManifestFetchData(kUpdateUrl, 0));
fetch->AddExtension(
- "1111", "1.0", &zeroDays, kEmptyUpdateUrlData, std::string());
+ "1111", "1.0", &zeroDays, kEmptyUpdateUrlData, std::string(), false);
// This will start the first fetcher.
downloader.StartUpdateCheck(fetch.Pass());
ExtensionUpdater::CheckParams params;
updater.Start();
updater.CheckNow(params);
+ content::RunAllBlockingPoolTasksUntilIdle();
// Make the updater do manifest fetching, and note the urls it tries to
// fetch.
extension->VersionString(),
&kNeverPingedData,
kEmptyUpdateUrlData,
- std::string());
+ std::string(),
+ false);
UpdateManifest::Results results;
results.daystart_elapsed_seconds = 750;
ExtensionUpdater::CheckParams params;
updater.Start();
updater.CheckNow(params);
+ content::RunAllBlockingPoolTasksUntilIdle();
}
TEST_F(ExtensionUpdaterTest, TestUpdatingDisabledExtensions) {
ExtensionUpdater::CheckParams params;
updater.Start();
updater.CheckNow(params);
+ content::RunAllBlockingPoolTasksUntilIdle();
}
TEST_F(ExtensionUpdaterTest, TestManifestFetchesBuilderAddExtension) {
const std::string& version,
const PingData* ping_data,
const std::string& update_url_data,
- const std::string& install_source) {
+ const std::string& install_source,
+ bool force_update) {
if (extension_ids_.find(id) != extension_ids_.end()) {
NOTREACHED() << "Duplicate extension id " << id;
return false;
}
+ if (force_update)
+ forced_updates_.insert(id);
+
+ // If we want to force an update, we send 0.0.0.0 as the installed version
+ // number.
+ const std::string installed_version = force_update ? "0.0.0.0" : version;
+
// Compute the string we'd append onto the full_url_, and see if it fits.
std::vector<std::string> parts;
parts.push_back("id=" + id);
- parts.push_back("v=" + version);
+ parts.push_back("v=" + installed_version);
if (!install_source.empty())
parts.push_back("installsource=" + install_source);
parts.push_back("uc");
request_ids_.insert(other.request_ids_.begin(), other.request_ids_.end());
}
+bool ManifestFetchData::DidForceUpdate(const std::string& extension_id) const {
+ return forced_updates_.find(extension_id) != forced_updates_.end();
+}
+
} // namespace extensions
const std::string& version,
const PingData* ping_data,
const std::string& update_url_data,
- const std::string& install_source);
+ const std::string& install_source,
+ bool force_update);
const GURL& base_url() const { return base_url_; }
const GURL& full_url() const { return full_url_; }
// to this ManifestFetchData).
void Merge(const ManifestFetchData& other);
+ // Returns |true| if a given extension was forced to update.
+ bool DidForceUpdate(const std::string& extension_id) const;
+
private:
// The set of extension id's for this ManifestFetchData.
std::set<std::string> extension_ids_;
// one ManifestFetchData.
std::set<int> request_ids_;
+ // The set of extension IDs for which this fetch forced a CRX update.
+ std::set<std::string> forced_updates_;
+
DISALLOW_COPY_AND_ASSIGN(ManifestFetchData);
};
class MTPDeviceDelegateImplLinux::MTPFileNode {
public:
MTPFileNode(uint32 file_id,
+ const std::string& file_name,
MTPFileNode* parent,
FileIdToMTPFileNodeMap* file_id_to_node_map);
~MTPFileNode();
bool DeleteChild(uint32 file_id);
uint32 file_id() const { return file_id_; }
+ const std::string& file_name() const { return file_name_; }
MTPFileNode* parent() { return parent_; }
private:
typedef base::ScopedPtrHashMap<std::string, MTPFileNode> ChildNodes;
const uint32 file_id_;
+ const std::string file_name_;
+
ChildNodes children_;
MTPFileNode* const parent_;
FileIdToMTPFileNodeMap* file_id_to_node_map_;
MTPDeviceDelegateImplLinux::MTPFileNode::MTPFileNode(
uint32 file_id,
+ const std::string& file_name,
MTPFileNode* parent,
FileIdToMTPFileNodeMap* file_id_to_node_map)
: file_id_(file_id),
+ file_name_(file_name),
parent_(parent),
file_id_to_node_map_(file_id_to_node_map) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
children_.set(
name,
- make_scoped_ptr(new MTPFileNode(id, this, file_id_to_node_map_)));
+ make_scoped_ptr(new MTPFileNode(id, name, this, file_id_to_node_map_)));
}
void MTPDeviceDelegateImplLinux::MTPFileNode::ClearNonexistentChildren(
task_in_progress_(false),
device_path_(device_location),
root_node_(new MTPFileNode(mtpd::kRootFileId,
- NULL,
+ "", // Root node has no name.
+ NULL, // And no parent node.
&file_id_to_node_map_)),
weak_ptr_factory_(this) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
const ErrorCallback& error_callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
DCHECK(!file_path.empty());
+
+ // If a ReadDirectory operation is in progress, the file info may already be
+ // cached.
+ FileInfoCache::const_iterator it = file_info_cache_.find(file_path);
+ if (it != file_info_cache_.end()) {
+ // TODO(thestig): This code is repeated in several places. Combine them.
+ // e.g. c/b/media_galleries/win/mtp_device_operations_util.cc
+ const fileapi::DirectoryEntry& cached_file_entry = it->second;
+ base::File::Info info;
+ info.size = cached_file_entry.size;
+ info.is_directory = cached_file_entry.is_directory;
+ info.is_symbolic_link = false;
+ info.last_modified = cached_file_entry.last_modified_time;
+ info.creation_time = base::Time();
+
+ success_callback.Run(info);
+ return;
+ }
base::Closure closure =
base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
weak_ptr_factory_.GetWeakPtr(),
DCHECK(it != file_id_to_node_map_.end());
MTPFileNode* dir_node = it->second;
+ // Traverse the MTPFileNode tree to reconstuct the full path for |dir_id|.
+ std::deque<std::string> dir_path_parts;
+ MTPFileNode* parent_node = dir_node;
+ while (parent_node->parent()) {
+ dir_path_parts.push_front(parent_node->file_name());
+ parent_node = parent_node->parent();
+ }
+ base::FilePath dir_path = device_path_;
+ for (size_t i = 0; i < dir_path_parts.size(); ++i)
+ dir_path = dir_path.Append(dir_path_parts[i]);
+
fileapi::AsyncFileUtil::EntryList normalized_file_list;
for (size_t i = 0; i < file_list.size(); ++i) {
normalized_file_list.push_back(file_list[i]);
// Refresh the in memory tree.
dir_node->EnsureChildExists(entry.name, file_id);
child_nodes_seen_.insert(entry.name);
+
+ // Add to |file_info_cache_|.
+ file_info_cache_[dir_path.Append(entry.name)] = entry;
}
success_callback.Run(normalized_file_list, has_more);
// Last call, finish book keeping and continue with the next request.
dir_node->ClearNonexistentChildren(child_nodes_seen_);
child_nodes_seen_.clear();
+ file_info_cache_.clear();
PendingRequestDone();
}
// Maps file ids to file nodes.
typedef std::map<uint32, MTPFileNode*> FileIdToMTPFileNodeMap;
+ // Maps file paths to file info.
+ typedef std::map<base::FilePath, fileapi::DirectoryEntry> FileInfoCache;
+
// Should only be called by CreateMTPDeviceAsyncDelegate() factory call.
// Defer the device initializations until the first file operation request.
// Do all the initializations in EnsureInitAndRunTask() function.
// can return results over multiple callbacks, is in progress.
std::set<std::string> child_nodes_seen_;
+ // A cache to store file metadata for file entries read during a ReadDirectory
+ // operation. Used to service incoming GetFileInfo calls for the duration of
+ // the ReadDirectory operation.
+ FileInfoCache file_info_cache_;
+
// For callbacks that may run after destruction.
base::WeakPtrFactory<MTPDeviceDelegateImplLinux> weak_ptr_factory_;
}
void ChromeNetworkDelegate::OnProxyFallback(const net::ProxyServer& bad_proxy,
- int net_error,
- bool did_fallback) {
+ int net_error) {
if (data_reduction_proxy_usage_stats_) {
data_reduction_proxy_usage_stats_->RecordBypassEventHistograms(
- bad_proxy, net_error, did_fallback);
+ bad_proxy, net_error);
}
}
RecordContentLengthHistograms(received_content_length,
original_content_length,
freshness_lifetime);
- if (data_reduction_proxy_enabled_ && data_reduction_proxy_usage_stats_) {
+ if (data_reduction_proxy_enabled_ &&
+ data_reduction_proxy_usage_stats_ &&
+ !proxy_config_getter_.is_null()) {
data_reduction_proxy_usage_stats_->RecordBypassedBytesHistograms(
- *request, *data_reduction_proxy_enabled_);
+ *request,
+ *data_reduction_proxy_enabled_,
+ proxy_config_getter_.Run());
}
DVLOG(2) << __FUNCTION__
<< " received content length: " << received_content_length
const net::ProxyService& proxy_service,
net::ProxyInfo* result) OVERRIDE;
virtual void OnProxyFallback(const net::ProxyServer& bad_proxy,
- int net_error,
- bool did_fallback) OVERRIDE;
+ int net_error) OVERRIDE;
virtual int OnBeforeSendHeaders(net::URLRequest* request,
const net::CompletionCallback& callback,
net::HttpRequestHeaders* headers) OVERRIDE;
#include "content/public/browser/browser_thread.h"
#include "net/base/network_change_notifier.h"
+namespace chrome_browser_net {
+
namespace {
// Since looking up preferences and current network connection are presumably
// both cheap, we do not cache them here.
-bool CanPrefetchAndPrerender(int network_prediction_options,
- bool network_prediction_enabled) {
+bool CanPrefetchAndPrerender(int network_prediction_options) {
switch (network_prediction_options) {
- case chrome_browser_net::NETWORK_PREDICTION_ALWAYS:
+ case NETWORK_PREDICTION_ALWAYS:
return true;
- case chrome_browser_net::NETWORK_PREDICTION_WIFI_ONLY:
- return !net::NetworkChangeNotifier::IsConnectionCellular(
- net::NetworkChangeNotifier::GetConnectionType());
- case chrome_browser_net::NETWORK_PREDICTION_NEVER:
+ case NETWORK_PREDICTION_NEVER:
return false;
- case chrome_browser_net::NETWORK_PREDICTION_UNSET:
- return network_prediction_enabled;
default:
- NOTREACHED() << "Unknown kNetworkPredictionOptions value.";
- return false;
+ DCHECK_EQ(NETWORK_PREDICTION_WIFI_ONLY, network_prediction_options);
+ return !net::NetworkChangeNotifier::IsConnectionCellular(
+ net::NetworkChangeNotifier::GetConnectionType());
}
}
-bool CanPreresolveAndPreconnect(int network_prediction_options,
- bool network_prediction_enabled) {
- switch (network_prediction_options) {
- case chrome_browser_net::NETWORK_PREDICTION_ALWAYS:
- return true;
- // DNS preresolution and TCP preconnect are performed even on cellular
- // networks if the user setting is WIFI_ONLY.
- case chrome_browser_net::NETWORK_PREDICTION_WIFI_ONLY:
- return true;
- case chrome_browser_net::NETWORK_PREDICTION_NEVER:
- return false;
- case chrome_browser_net::NETWORK_PREDICTION_UNSET:
- return network_prediction_enabled;
- default:
- NOTREACHED() << "Unknown kNetworkPredictionOptions value.";
- return false;
- }
+bool CanPreresolveAndPreconnect(int network_prediction_options) {
+ // DNS preresolution and TCP preconnect are performed even on cellular
+ // networks if the user setting is WIFI_ONLY.
+ return network_prediction_options != NETWORK_PREDICTION_NEVER;
}
} // namespace
-namespace chrome_browser_net {
-
void RegisterPredictionOptionsProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterIntegerPref(
prefs::kNetworkPredictionOptions,
- chrome_browser_net::NETWORK_PREDICTION_UNSET,
+ NETWORK_PREDICTION_DEFAULT,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
}
if (network_prediction_enabled->GetAsBoolean(&value)) {
pref_service->SetInteger(
prefs::kNetworkPredictionOptions,
- value ? chrome_browser_net::NETWORK_PREDICTION_WIFI_ONLY
- : chrome_browser_net::NETWORK_PREDICTION_NEVER);
+ value ? NETWORK_PREDICTION_WIFI_ONLY : NETWORK_PREDICTION_NEVER);
}
}
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
DCHECK(profile_io_data);
return CanPrefetchAndPrerender(
- profile_io_data->network_prediction_options()->GetValue(),
- profile_io_data->network_prediction_enabled()->GetValue());
+ profile_io_data->network_prediction_options()->GetValue());
}
bool CanPrefetchAndPrerenderUI(PrefService* prefs) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(prefs);
return CanPrefetchAndPrerender(
- prefs->GetInteger(prefs::kNetworkPredictionOptions),
- prefs->GetBoolean(prefs::kNetworkPredictionEnabled));
+ prefs->GetInteger(prefs::kNetworkPredictionOptions));
}
bool CanPredictNetworkActionsUI(PrefService* prefs) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
DCHECK(profile_io_data);
return CanPreresolveAndPreconnect(
- profile_io_data->network_prediction_options()->GetValue(),
- profile_io_data->network_prediction_enabled()->GetValue());
+ profile_io_data->network_prediction_options()->GetValue());
}
bool CanPreresolveAndPreconnectUI(PrefService* prefs) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(prefs);
return CanPreresolveAndPreconnect(
- prefs->GetInteger(prefs::kNetworkPredictionOptions),
- prefs->GetBoolean(prefs::kNetworkPredictionEnabled));
+ prefs->GetInteger(prefs::kNetworkPredictionOptions));
}
} // namespace chrome_browser_net
// Enum describing when to allow network predictions based on connection type.
// The numerical value is stored in the prefs file, therefore the same enum
// with the same order must be used by the platform-dependent components.
-// TODO(bnc): implement as per crbug.com/334602.
-// NETWORK_PREDICTION_UNSET means that the old preferences,
-// kNetworkPredictionEnabled and kAllowPrerender, should be observed.
enum NetworkPredictionOptions {
NETWORK_PREDICTION_ALWAYS,
NETWORK_PREDICTION_WIFI_ONLY,
NETWORK_PREDICTION_NEVER,
- NETWORK_PREDICTION_UNSET,
+ NETWORK_PREDICTION_DEFAULT = NETWORK_PREDICTION_WIFI_ONLY,
};
void RegisterPredictionOptionsProfilePrefs(
template <class C>
void data_reduction_proxy::DataReductionProxySettingsTestBase::SetProbeResult(
const std::string& test_url,
- const std::string& warmup_test_url,
const std::string& response,
ProbeURLFetchResult result,
bool success,
template void
data_reduction_proxy::DataReductionProxySettingsTestBase::SetProbeResult<
DataReductionProxyChromeSettings>(const std::string& test_url,
- const std::string& warmup_test_url,
const std::string& response,
ProbeURLFetchResult result,
bool success,
bool ChromePasswordManagerClient::IsPasswordManagerEnabledForCurrentPage()
const {
- if (EnabledForSyncSignin())
- return true;
-
DCHECK(web_contents());
content::NavigationEntry* entry =
web_contents()->GetController().GetLastCommittedEntry();
// TODO(gcasto): Determine if fix for crbug.com/388246 is relevant here.
return true;
}
+
+ // Disable the password manager for online password management.
+ if (IsURLPasswordWebsiteReauth(entry->GetURL()))
+ return false;
+
+ if (EnabledForSyncSignin())
+ return true;
+
// Do not fill nor save password when a user is signing in for sync. This
// is because users need to remember their password if they are syncing as
// this is effectively their master password.
&ignored_value);
}
+bool ChromePasswordManagerClient::IsURLPasswordWebsiteReauth(
+ const GURL& url) const {
+ if (url.GetOrigin() != GaiaUrls::GetInstance()->gaia_url().GetOrigin())
+ return false;
+
+ // "rart" param signals this page is for transactional reauth.
+ std::string param_value;
+ if (!net::GetValueForKeyInQuery(url, "rart", ¶m_value))
+ return false;
+
+ // Check the "continue" param to see if this reauth page is for the passwords
+ // website.
+ param_value.clear();
+ if (!net::GetValueForKeyInQuery(url, "continue", ¶m_value))
+ return false;
+
+ return GURL(param_value).host() ==
+ GURL(chrome::kPasswordManagerAccountDashboardURL).host();
+}
+
bool ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled() {
#if !defined(USE_AURA)
return false;
// Google property.
bool LastLoadWasTransactionalReauthPage() const;
+ // Returns true if |url| is the reauth page for accessing the password
+ // website.
+ bool IsURLPasswordWebsiteReauth(const GURL& url) const;
+
// Sets |autofill_state_| based on experiment and flag values.
void SetUpAutofillSyncState();
NavigateAndCommit(GURL("https://accounts.google.com/Login"));
EXPECT_TRUE(client->ShouldFilterAutofillResult(form));
}
+
+TEST_F(ChromePasswordManagerClientTest,
+ IsPasswordManagerEnabledForCurrentPage) {
+ ChromePasswordManagerClient* client = GetClient();
+ NavigateAndCommit(
+ GURL("https://accounts.google.com/ServiceLogin?continue="
+ "https://passwords.google.com/settings&rart=123"));
+ EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
+
+ // Password site is inaccesible via HTTP, but because of HSTS the following
+ // link should still continue to https://passwords.google.com.
+ NavigateAndCommit(
+ GURL("https://accounts.google.com/ServiceLogin?continue="
+ "http://passwords.google.com/settings&rart=123"));
+ EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
+
+ // Specifying default port still passes.
+ NavigateAndCommit(
+ GURL("https://accounts.google.com/ServiceLogin?continue="
+ "https://passwords.google.com:443/settings&rart=123"));
+ EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
+
+ // Encoded URL is considered the same.
+ NavigateAndCommit(
+ GURL("https://accounts.google.com/ServiceLogin?continue="
+ "https://passwords.%67oogle.com/settings&rart=123"));
+ EXPECT_FALSE(client->IsPasswordManagerEnabledForCurrentPage());
+
+ // Fully qualified domain name is considered a different hostname by GURL.
+ // Ideally this would not be the case, but this quirk can be avoided by
+ // verification on the server. This test is simply documentation of this
+ // behavior.
+ NavigateAndCommit(
+ GURL("https://accounts.google.com/ServiceLogin?continue="
+ "https://passwords.google.com./settings&rart=123"));
+ EXPECT_TRUE(client->IsPasswordManagerEnabledForCurrentPage());
+
+ // Not a transactional reauth page.
+ NavigateAndCommit(
+ GURL("https://accounts.google.com/ServiceLogin?continue="
+ "https://passwords.google.com/settings"));
+ EXPECT_TRUE(client->IsPasswordManagerEnabledForCurrentPage());
+
+ // Should be enabled for other transactional reauth pages.
+ NavigateAndCommit(
+ GURL("https://accounts.google.com/ServiceLogin?continue="
+ "https://mail.google.com&rart=234"));
+ EXPECT_TRUE(client->IsPasswordManagerEnabledForCurrentPage());
+
+ // Reauth pages are only on accounts.google.com
+ NavigateAndCommit(
+ GURL("https://other.site.com/ServiceLogin?continue="
+ "https://passwords.google.com&rart=234"));
+ EXPECT_TRUE(client->IsPasswordManagerEnabledForCurrentPage());
+}
second_observer.Wait();
EXPECT_FALSE(second_prompt_observer->IsShowingPrompt());
- // Verify that we sent a ping to Autofill saying that the original form
- // was likely an account creation form since it has more than 2 text input
- // fields and was used for the first time on a different form.
+ // Verify that we sent two pings to Autofill. One vote for of PASSWORD for
+ // the current form, and one vote for ACCOUNT_CREATION_PASSWORD on the
+ // original form since it has more than 2 text input fields and was used for
+ // the first time on a different form.
base::HistogramBase* upload_histogram =
base::StatisticsRecorder::FindHistogram(
"PasswordGeneration.UploadStarted");
scoped_ptr<base::HistogramSamples> snapshot =
upload_histogram->SnapshotSamples();
EXPECT_EQ(0, snapshot->GetCount(0 /* failure */));
- EXPECT_EQ(1, snapshot->GetCount(1 /* success */));
+ EXPECT_EQ(2, snapshot->GetCount(1 /* success */));
}
#endif
POLICY_SCOPE_USER,
new base::StringValue("primary-only"),
NULL);
+ policy_map->Set(key::kEasyUnlockAllowed,
+ POLICY_LEVEL_MANDATORY,
+ POLICY_SCOPE_USER,
+ new base::FundamentalValue(false),
+ NULL);
#endif
}
POLICY_SCOPE_USER,
new base::StringValue("primary-only"),
NULL);
+ expected->Set(key::kEasyUnlockAllowed,
+ POLICY_LEVEL_MANDATORY,
+ POLICY_SCOPE_USER,
+ new base::FundamentalValue(false),
+ NULL);
#endif
}
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
- DCHECK(io_data);
-
- // TODO(bnc): Remove this condition once the new
- // predictive preference is used on all platforms. See crbug.com/334602.
- if (io_data->network_prediction_options()->GetValue() ==
- chrome_browser_net::NETWORK_PREDICTION_UNSET &&
- net::NetworkChangeNotifier::IsConnectionCellular(
- net::NetworkChangeNotifier::GetConnectionType())) {
- return false;
- }
-
return chrome_browser_net::CanPrefetchAndPrerenderIO(io_data) &&
!DisableForFieldTrial();
}
#include "base/prefs/pref_service.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/net/prediction_options.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
+#include "net/base/network_change_notifier.h"
#include "net/url_request/url_request_filter.h"
#include "net/url_request/url_request_job.h"
+using chrome_browser_net::NetworkPredictionOptions;
using content::BrowserThread;
+using net::NetworkChangeNotifier;
namespace {
const char kPrefetchPage[] = "files/prerender/simple_prefetch.html";
+class MockNetworkChangeNotifierWIFI : public NetworkChangeNotifier {
+ public:
+ virtual ConnectionType GetCurrentConnectionType() const OVERRIDE {
+ return NetworkChangeNotifier::CONNECTION_WIFI;
+ }
+};
+
+class MockNetworkChangeNotifier4G : public NetworkChangeNotifier {
+ public:
+ virtual ConnectionType GetCurrentConnectionType() const OVERRIDE {
+ return NetworkChangeNotifier::CONNECTION_4G;
+ }
+};
+
class PrefetchBrowserTestBase : public InProcessBrowserTest {
public:
- explicit PrefetchBrowserTestBase(bool do_predictive_networking,
- bool do_prefetch_field_trial)
- : do_predictive_networking_(do_predictive_networking),
- do_prefetch_field_trial_(do_prefetch_field_trial) {}
+ explicit PrefetchBrowserTestBase(bool disabled_via_field_trial)
+ : disabled_via_field_trial_(disabled_via_field_trial) {}
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
- if (do_prefetch_field_trial_) {
+ if (disabled_via_field_trial_) {
command_line->AppendSwitchASCII(switches::kForceFieldTrials,
"Prefetch/ExperimentDisabled/");
- } else {
- command_line->AppendSwitchASCII(switches::kForceFieldTrials,
- "Prefetch/ExperimentEnabled/");
}
}
- virtual void SetUpOnMainThread() OVERRIDE {
- browser()->profile()->GetPrefs()->SetBoolean(
- prefs::kNetworkPredictionEnabled, do_predictive_networking_);
+ void SetPreference(NetworkPredictionOptions value) {
+ browser()->profile()->GetPrefs()->SetInteger(
+ prefs::kNetworkPredictionOptions, value);
}
bool RunPrefetchExperiment(bool expect_success, Browser* browser) {
- CHECK(test_server()->Start());
GURL url = test_server()->GetURL(kPrefetchPage);
const base::string16 expected_title =
}
private:
- bool do_predictive_networking_;
- bool do_prefetch_field_trial_;
-};
-
-class PrefetchBrowserTestPredictionOnExpOn : public PrefetchBrowserTestBase {
- public:
- PrefetchBrowserTestPredictionOnExpOn()
- : PrefetchBrowserTestBase(true, true) {}
-};
-
-class PrefetchBrowserTestPredictionOnExpOff : public PrefetchBrowserTestBase {
- public:
- PrefetchBrowserTestPredictionOnExpOff()
- : PrefetchBrowserTestBase(true, false) {}
+ bool disabled_via_field_trial_;
};
-class PrefetchBrowserTestPredictionOffExpOn : public PrefetchBrowserTestBase {
+class PrefetchBrowserTestPrediction : public PrefetchBrowserTestBase {
public:
- PrefetchBrowserTestPredictionOffExpOn()
- : PrefetchBrowserTestBase(false, true) {}
+ PrefetchBrowserTestPrediction() : PrefetchBrowserTestBase(false) {}
};
-class PrefetchBrowserTestPredictionOffExpOff : public PrefetchBrowserTestBase {
+class PrefetchBrowserTestPredictionDisabled : public PrefetchBrowserTestBase {
public:
- PrefetchBrowserTestPredictionOffExpOff()
- : PrefetchBrowserTestBase(false, false) {}
+ PrefetchBrowserTestPredictionDisabled() : PrefetchBrowserTestBase(true) {}
};
// URLRequestJob (and associated handler) which hangs.
url, never_respond_handler.Pass());
}
-// Privacy option is on, experiment is on. Prefetch should succeed.
-IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPredictionOnExpOn, PredOnExpOn) {
+// Prefetch is disabled via field experiment. Prefetch should be dropped.
+IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPredictionDisabled,
+ ExperimentDisabled) {
+ CHECK(test_server()->Start());
+ EXPECT_TRUE(RunPrefetchExperiment(false, browser()));
+ // Should not prefetch even if preference is ALWAYS.
+ SetPreference(NetworkPredictionOptions::NETWORK_PREDICTION_ALWAYS);
EXPECT_TRUE(RunPrefetchExperiment(false, browser()));
}
-// Privacy option is on, experiment is off. Prefetch should be dropped.
-IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPredictionOnExpOff, PredOnExpOff) {
- EXPECT_TRUE(RunPrefetchExperiment(true, browser()));
-}
+// Prefetch should be allowed depending on preference and network type.
+IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPrediction, PreferenceWorks) {
+ CHECK(test_server()->Start());
+ // Set real NetworkChangeNotifier singleton aside.
+ scoped_ptr<NetworkChangeNotifier::DisableForTest> disable_for_test(
+ new NetworkChangeNotifier::DisableForTest);
+
+ // Preference defaults to WIFI_ONLY: prefetch when not on cellular.
+ {
+ scoped_ptr<NetworkChangeNotifier> mock(new MockNetworkChangeNotifierWIFI);
+ EXPECT_TRUE(RunPrefetchExperiment(true, browser()));
+ }
+ {
+ scoped_ptr<NetworkChangeNotifier> mock(new MockNetworkChangeNotifier4G);
+ EXPECT_TRUE(RunPrefetchExperiment(false, browser()));
+ }
-// Privacy option is off, experiment is on. Prefetch should be dropped.
-IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPredictionOffExpOn, PredOffExpOn) {
- EXPECT_TRUE(RunPrefetchExperiment(false, browser()));
-}
+ // Set preference to ALWAYS: always prefetch.
+ SetPreference(NetworkPredictionOptions::NETWORK_PREDICTION_ALWAYS);
+ {
+ scoped_ptr<NetworkChangeNotifier> mock(new MockNetworkChangeNotifierWIFI);
+ EXPECT_TRUE(RunPrefetchExperiment(true, browser()));
+ }
+ {
+ scoped_ptr<NetworkChangeNotifier> mock(new MockNetworkChangeNotifier4G);
+ EXPECT_TRUE(RunPrefetchExperiment(true, browser()));
+ }
-// Privacy option is off, experiment is off. Prefetch should be dropped.
-IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPredictionOffExpOff, PredOffExpOff) {
- EXPECT_TRUE(RunPrefetchExperiment(false, browser()));
+ // Set preference to NEVER: never prefetch.
+ SetPreference(NetworkPredictionOptions::NETWORK_PREDICTION_NEVER);
+ {
+ scoped_ptr<NetworkChangeNotifier> mock(new MockNetworkChangeNotifierWIFI);
+ EXPECT_TRUE(RunPrefetchExperiment(false, browser()));
+ }
+ {
+ scoped_ptr<NetworkChangeNotifier> mock(new MockNetworkChangeNotifier4G);
+ EXPECT_TRUE(RunPrefetchExperiment(false, browser()));
+ }
}
// Bug 339909: When in incognito mode the browser crashed due to an
// uninitialized preference member. Verify that it no longer does.
-IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPredictionOnExpOff, IncognitoTest) {
+IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPrediction, IncognitoTest) {
Profile* incognito_profile = browser()->profile()->GetOffTheRecordProfile();
Browser* incognito_browser = new Browser(
Browser::CreateParams(incognito_profile, browser()->host_desktop_type()));
// WebContents for the incognito browser.
ui_test_utils::OpenURLOffTheRecord(browser()->profile(), GURL("about:blank"));
+ CHECK(test_server()->Start());
EXPECT_TRUE(RunPrefetchExperiment(true, incognito_browser));
}
// - if a prefetch is in progress, but the originating renderer is destroyed,
// that the pending prefetch request is cleaned up cleanly and does not
// result in a crash.
-IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPredictionOnExpOff,
- PrefetchFromBrowser) {
+IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPrediction, PrefetchFromBrowser) {
const GURL kHangingUrl("http://hanging-url.com");
base::RunLoop loop_;
BrowserThread::PostTask(BrowserThread::IO,
// static
bool IncognitoModePrefs::CanOpenBrowser(Profile* profile) {
+ if (profile->IsGuestSession())
+ return true;
+
switch (GetAvailability(profile->GetPrefs())) {
case IncognitoModePrefs::ENABLED:
return true;
PrerenderManager::PrerenderManager(Profile* profile,
PrerenderTracker* prerender_tracker)
- : enabled_(profile && profile->GetPrefs() &&
- profile->GetPrefs()->GetBoolean(prefs::kNetworkPredictionEnabled)),
+ : enabled_(true),
profile_(profile),
prerender_tracker_(prerender_tracker),
prerender_contents_factory_(PrerenderContents::CreateFactory()),
bool PrerenderManager::IsEnabled() const {
DCHECK(CalledOnValidThread());
- // TODO(bnc): remove conditional as per crbug.com/334602.
- if (profile_ && profile_->GetPrefs() &&
- profile_->GetPrefs()->GetInteger(prefs::kNetworkPredictionOptions) !=
- chrome_browser_net::NETWORK_PREDICTION_UNSET) {
- return chrome_browser_net::CanPrefetchAndPrerenderUI(profile_->GetPrefs());
- }
- // TODO(bnc): remove rest of method as per crbug.com/334602.
if (!enabled_)
return false;
- for (std::list<const PrerenderCondition*>::const_iterator it =
- prerender_conditions_.begin();
- it != prerender_conditions_.end();
- ++it) {
- const PrerenderCondition* condition = *it;
- if (!condition->CanPrerender())
- return false;
- }
- return true;
+ return chrome_browser_net::CanPrefetchAndPrerenderUI(profile_->GetPrefs());
}
void PrerenderManager::AddProfileNetworkBytesIfEnabled(int64 bytes) {
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/utility_process_host.h"
#include "content/public/browser/utility_process_host_client.h"
+#include "printing/emf_win.h"
#include "printing/page_range.h"
#include "printing/pdf_render_settings.h"
using content::BrowserThread;
-class FileHandlers {
+class FileHandlers
+ : public base::RefCountedThreadSafe<FileHandlers,
+ BrowserThread::DeleteOnFileThread> {
public:
FileHandlers() {}
- ~FileHandlers() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- }
-
void Init(base::RefCountedMemory* data);
bool IsValid();
return temp_dir_.path().AppendASCII("output.emf");
}
+ base::FilePath GetEmfPagePath(int page_number) const {
+ return GetEmfPath().InsertBeforeExtensionASCII(
+ base::StringPrintf(".%d", page_number));
+ }
+
base::FilePath GetPdfPath() const {
return temp_dir_.path().AppendASCII("input.pdf");
}
}
private:
+ friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>;
+ friend class base::DeleteHelper<FileHandlers>;
+
+ ~FileHandlers() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); }
+
base::ScopedTempDir temp_dir_;
base::File pdf_file_;
};
return;
}
+ pdf_file_.Initialize(GetPdfPath(),
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
+ base::File::FLAG_READ |
+ base::File::FLAG_DELETE_ON_CLOSE);
if (static_cast<int>(data->size()) !=
- base::WriteFile(GetPdfPath(), data->front_as<char>(), data->size())) {
+ pdf_file_.WriteAtCurrentPos(data->front_as<char>(), data->size())) {
+ pdf_file_.Close();
return;
}
-
- // Reopen in read only mode.
- pdf_file_.Initialize(GetPdfPath(),
- base::File::FLAG_OPEN | base::File::FLAG_READ);
+ pdf_file_.Seek(base::File::FROM_BEGIN, 0);
+ pdf_file_.Flush();
}
bool FileHandlers::IsValid() {
return pdf_file_.IsValid();
}
+// Modification of Emf to keep references to |FileHandlers|.
+// |FileHandlers| must be deleted after the last metafile is closed because
+// Emf holds files locked.
+// Ideally we want to use FLAG_DELETE_ON_CLOSE, but it requires large changes.
+// It's going to be done for crbug.com/408184
+class TempEmf : public Emf {
+ public:
+ explicit TempEmf(const scoped_refptr<FileHandlers>& files) : files_(files) {}
+ virtual ~TempEmf() {}
+
+ virtual bool SafePlayback(HDC hdc) const OVERRIDE {
+ bool result = Emf::SafePlayback(hdc);
+ TempEmf* this_mutable = const_cast<TempEmf*>(this);
+ // TODO(vitalybuka): Fix destruction of metafiles. For some reasons
+ // instances of Emf are not deleted. crbug.com/411683
+ // |files_| must be released as soon as possible to guarantee deletion.
+ // It's know that this Emf file is going to be played just once to
+ // a printer. So just release files here.
+ this_mutable->Close();
+ this_mutable->files_ = NULL;
+ return result;
+ }
+
+ private:
+ scoped_refptr<FileHandlers> files_;
+ DISALLOW_COPY_AND_ASSIGN(TempEmf);
+};
+
// Converts PDF into EMF.
// Class uses 3 threads: UI, IO and FILE.
// Internal workflow is following:
double scale_factor);
void OnFilesReadyOnUIThread();
- scoped_ptr<FileHandlers> files_;
+ scoped_refptr<FileHandlers> files_;
printing::PdfRenderSettings settings_;
PdfToEmfConverter::ResultCallback callback_;
base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
: settings_(settings) {}
PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() {
- // Delete temp directory.
- BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release());
}
void PdfToEmfUtilityProcessHostClient::Convert(
const PdfToEmfConverter::ResultCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
callback_ = callback;
- CHECK(!files_);
- files_.reset(new FileHandlers());
+ CHECK(!files_.get());
+ files_ = new FileHandlers();
BrowserThread::PostTaskAndReply(
BrowserThread::FILE,
FROM_HERE,
- base::Bind(&FileHandlers::Init,
- base::Unretained(files_.get()),
- make_scoped_refptr(data)),
+ base::Bind(&FileHandlers::Init, files_, make_scoped_refptr(data)),
base::Bind(&PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread,
this));
}
const std::vector<printing::PageRange>& page_ranges,
double scale_factor) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- std::vector<base::FilePath> page_filenames;
+ ScopedVector<Metafile> pages;
std::vector<printing::PageRange>::const_iterator iter;
for (iter = page_ranges.begin(); iter != page_ranges.end(); ++iter) {
for (int page_number = iter->from; page_number <= iter->to; ++page_number) {
- page_filenames.push_back(files_->GetEmfPath().InsertBeforeExtensionASCII(
- base::StringPrintf(".%d", page_number)));
+ scoped_ptr<TempEmf> metafile(new TempEmf(files_));
+ if (!metafile->InitFromFile(files_->GetEmfPagePath(page_number))) {
+ NOTREACHED() << "Invalid metafile";
+ metafile.reset();
+ }
+ pages.push_back(metafile.release());
}
}
+ files_ = NULL;
if (!callback_.is_null()) {
- BrowserThread::PostTask(
- BrowserThread::UI,
- FROM_HERE,
- base::Bind(callback_, scale_factor, page_filenames));
+ callback_.Run(scale_factor, &pages);
callback_.Reset();
}
}
#include "base/callback.h"
#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_vector.h"
namespace base {
class FilePath;
namespace printing {
+class Metafile;
+
class PdfToEmfConverter {
public:
// Callback for when the PDF is converted to an EMF.
- typedef base::Callback<void(double /*scale_factor*/,
- const std::vector<base::FilePath>& /*emf_files*/)>
+ // Takes ownership of metafiles.
+ typedef base::Callback<
+ void(double /*scale_factor*/, ScopedVector<Metafile>* /*emf_files*/)>
ResultCallback;
virtual ~PdfToEmfConverter() {}
#include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
#include "chrome/browser/plugins/chrome_plugin_service_filter.h"
#include "chrome/browser/printing/print_view_manager.h"
-#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/webplugininfo.h"
#include "ui/web_dialogs/web_dialog_delegate.h"
-#include "ui/web_dialogs/web_dialog_web_contents_delegate.h"
-using content::NativeWebKeyboardEvent;
using content::NavigationController;
using content::WebContents;
using content::WebUIMessageHandler;
-using ui::WebDialogDelegate;
-using ui::WebDialogWebContentsDelegate;
namespace {
GURL(), pdf_plugin);
}
-// WebDialogDelegate that specifies what the print preview dialog
-// will look like.
-class PrintPreviewDialogDelegate : public WebDialogDelegate {
+// A ui::WebDialogDelegate that specifies the print preview dialog appearance.
+class PrintPreviewDialogDelegate : public ui::WebDialogDelegate {
public:
explicit PrintPreviewDialogDelegate(WebContents* initiator);
virtual ~PrintPreviewDialogDelegate();
return false;
}
-// WebContentsDelegate that forwards shortcut keys in the print preview
-// renderer to the browser.
-class PrintPreviewWebContentDelegate : public WebDialogWebContentsDelegate {
- public:
- PrintPreviewWebContentDelegate(Profile* profile, WebContents* initiator);
- virtual ~PrintPreviewWebContentDelegate();
-
- // Overridden from WebDialogWebContentsDelegate:
- virtual void HandleKeyboardEvent(
- WebContents* source,
- const NativeWebKeyboardEvent& event) OVERRIDE;
-
- private:
- WebContents* initiator_;
-
- DISALLOW_COPY_AND_ASSIGN(PrintPreviewWebContentDelegate);
-};
-
-PrintPreviewWebContentDelegate::PrintPreviewWebContentDelegate(
- Profile* profile,
- WebContents* initiator)
- : WebDialogWebContentsDelegate(profile, new ChromeWebContentsHandler),
- initiator_(initiator) {}
-
-PrintPreviewWebContentDelegate::~PrintPreviewWebContentDelegate() {}
-
-void PrintPreviewWebContentDelegate::HandleKeyboardEvent(
- WebContents* source,
- const NativeWebKeyboardEvent& event) {
- // Disabled on Mac due to http://crbug.com/112173
-#if !defined(OS_MACOSX)
- Browser* current_browser = chrome::FindBrowserWithWebContents(initiator_);
- if (!current_browser)
- return;
- current_browser->window()->HandleKeyboardEvent(event);
-#endif
-}
-
} // namespace
namespace printing {
WebContents* PrintPreviewDialogController::CreatePrintPreviewDialog(
WebContents* initiator) {
base::AutoReset<bool> auto_reset(&is_creating_print_preview_dialog_, true);
- Profile* profile =
- Profile::FromBrowserContext(initiator->GetBrowserContext());
-
- // |web_dialog_ui_delegate| deletes itself in
- // PrintPreviewDialogDelegate::OnDialogClosed().
- WebDialogDelegate* web_dialog_delegate =
- new PrintPreviewDialogDelegate(initiator);
- // |web_dialog_delegate|'s owner is |constrained_delegate|.
- PrintPreviewWebContentDelegate* pp_wcd =
- new PrintPreviewWebContentDelegate(profile, initiator);
- ConstrainedWebDialogDelegate* constrained_delegate =
- CreateConstrainedWebDialog(profile,
- web_dialog_delegate,
- pp_wcd,
+
+ // The dialog delegates are deleted when the dialog is closed.
+ ConstrainedWebDialogDelegate* web_dialog_delegate =
+ CreateConstrainedWebDialog(initiator->GetBrowserContext(),
+ new PrintPreviewDialogDelegate(initiator),
initiator);
- WebContents* preview_dialog = constrained_delegate->GetWebContents();
+
+ WebContents* preview_dialog = web_dialog_delegate->GetWebContents();
EnableInternalPDFPluginForContents(preview_dialog);
PrintViewManager::CreateForWebContents(preview_dialog);
extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
void PrintViewManagerBase::OnPdfToEmfConverted(
const PrintHostMsg_DidPrintPage_Params& params,
double scale_factor,
- const std::vector<base::FilePath>& emf_files) {
+ ScopedVector<Metafile>* emf_files) {
if (!print_job_.get())
return;
if (!document)
return;
- for (size_t i = 0; i < emf_files.size(); ++i) {
- scoped_ptr<printing::Emf> metafile(new printing::Emf);
- if (!metafile->InitFromFile(emf_files[i])) {
- NOTREACHED() << "Invalid metafile";
+ for (size_t i = 0; i < emf_files->size(); ++i) {
+ if (!(*emf_files)[i]) {
web_contents()->Stop();
return;
}
+ }
+
+ for (size_t i = 0; i < emf_files->size(); ++i) {
// Update the rendered document. It will send notifications to the listener.
document->SetPage(i,
- metafile.release(),
+ (*emf_files)[i],
scale_factor,
params.page_size,
params.content_area);
}
+ // document->SetPage took ownership of all EMFs.
+ emf_files->weak_clear();
ShouldQuitFromInnerMessageLoop();
}
namespace printing {
class JobEventDetails;
+class Metafile;
class PdfToEmfConverter;
class PrintJob;
class PrintJobWorkerOwner;
// Called on completion of converting the pdf to emf.
void OnPdfToEmfConverted(const PrintHostMsg_DidPrintPage_Params& params,
double scale_factor,
- const std::vector<base::FilePath>& emf_file);
+ ScopedVector<Metafile>* emf_files);
#endif
content::NotificationRegistrar registrar_;
if (username.empty()) {
// Unset the old user's GAIA info.
cache.SetGAIANameOfProfileAtIndex(profile_index, base::string16());
+ cache.SetGAIAGivenNameOfProfileAtIndex(profile_index, base::string16());
// The profile index may have changed.
profile_index = cache.GetIndexOfProfileWithPath(profile_->GetPath());
if (profile_index == std::string::npos)
BrowserContextDependencyManager::GetInstance()->CreateBrowserContextServices(
this);
- DCHECK_NE(IncognitoModePrefs::DISABLED,
- IncognitoModePrefs::GetAvailability(profile_->GetPrefs()));
+ // Guest profiles may always be OTR. Check IncognitoModePrefs otherwise.
+ DCHECK(profile_->IsGuestSession() ||
+ IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) !=
+ IncognitoModePrefs::DISABLED);
#if defined(OS_ANDROID) || defined(OS_IOS)
UseSystemProxy();
return profile_android->obj_.obj();
}
+base::android::ScopedJavaLocalRef<jobject> ProfileAndroid::GetOriginalProfile(
+ JNIEnv* env, jobject obj) {
+ ProfileAndroid* original_profile = ProfileAndroid::FromProfile(
+ profile_->GetOriginalProfile());
+ DCHECK(original_profile);
+ return original_profile->GetJavaObject();
+}
+
+base::android::ScopedJavaLocalRef<jobject>
+ProfileAndroid::GetOffTheRecordProfile(JNIEnv* env, jobject obj) {
+ ProfileAndroid* otr_profile = ProfileAndroid::FromProfile(
+ profile_->GetOffTheRecordProfile());
+ DCHECK(otr_profile);
+ return otr_profile->GetJavaObject();
+}
+
+jboolean ProfileAndroid::HasOffTheRecordProfile(JNIEnv* env, jobject obj) {
+ return profile_->HasOffTheRecordProfile();
+}
+
+jboolean ProfileAndroid::IsOffTheRecord(JNIEnv* env, jobject obj) {
+ return profile_->IsOffTheRecord();
+}
+
// static
jobject GetLastUsedProfile(JNIEnv* env, jclass clazz) {
return ProfileAndroid::GetLastUsedProfile(env, clazz);
base::android::ScopedJavaLocalRef<jobject> jprofile =
Java_Profile_create(env, reinterpret_cast<intptr_t>(this));
obj_.Reset(env, jprofile.obj());
-
}
ProfileAndroid::~ProfileAndroid() {
static jobject GetLastUsedProfile(JNIEnv* env, jclass clazz);
+ // Return the original profile.
+ base::android::ScopedJavaLocalRef<jobject> GetOriginalProfile(
+ JNIEnv* env, jobject obj);
+
+ // Return the incognito profile.
+ //
+ // WARNING: This will create the OffTheRecord profile if it doesn't already
+ // exist. If this isn't what you want, you need to check
+ // HasOffTheRecordProfile() first.
+ base::android::ScopedJavaLocalRef<jobject> GetOffTheRecordProfile(
+ JNIEnv* env, jobject obj);
+
+ // Return whether an off the record profile exists.
+ jboolean HasOffTheRecordProfile(JNIEnv* env, jobject obj);
+
+ // Whether this profile is off the record.
+ jboolean IsOffTheRecord(JNIEnv* env, jobject obj);
+
explicit ProfileAndroid(Profile* profile);
virtual ~ProfileAndroid();
}
}
- // If needed, start downloading the high-res avatars.
- if (switches::IsNewAvatarMenu()) {
- for (size_t i = 0; i < GetNumberOfProfiles(); i++) {
- DownloadHighResAvatar(GetAvatarIconIndexOfProfileAtIndex(i),
- GetPathOfProfileAtIndex(i));
- }
- }
+ // If needed, start downloading the high-res avatars and migrate any legacy
+ // profile names.
+ if (switches::IsNewAvatarMenu())
+ MigrateLegacyProfileNamesAndDownloadAvatars();
}
ProfileInfoCache::~ProfileInfoCache() {
delete avatar_images_downloads_in_progress_[file_name];
avatar_images_downloads_in_progress_[file_name] = NULL;
}
+
+void ProfileInfoCache::MigrateLegacyProfileNamesAndDownloadAvatars() {
+ DCHECK(switches::IsNewAvatarMenu());
+
+ // Only do this on desktop platforms.
+#if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS)
+ // Migrate any legacy profile names ("First user", "Default Profile") to
+ // new style default names ("Person 1"). The problem here is that every
+ // time you rename a profile, the ProfileInfoCache sorts itself, so
+ // whatever you were iterating through is no longer valid. We need to
+ // save a list of the profile paths (which thankfully do not change) that
+ // need to be renamed. We also can't pre-compute the new names, as they
+ // depend on the names of all the other profiles in the info cache, so they
+ // need to be re-computed after each rename.
+ std::vector<base::FilePath> profiles_to_rename;
+
+ const base::string16 default_profile_name = base::i18n::ToLower(
+ l10n_util::GetStringUTF16(IDS_DEFAULT_PROFILE_NAME));
+ const base::string16 default_legacy_profile_name = base::i18n::ToLower(
+ l10n_util::GetStringUTF16(IDS_LEGACY_DEFAULT_PROFILE_NAME));
+
+ for (size_t i = 0; i < GetNumberOfProfiles(); i++) {
+ // If needed, start downloading the high-res avatar for this profile.
+ DownloadHighResAvatar(GetAvatarIconIndexOfProfileAtIndex(i),
+ GetPathOfProfileAtIndex(i));
+
+ base::string16 name = base::i18n::ToLower(GetNameOfProfileAtIndex(i));
+ if (name == default_profile_name || name == default_legacy_profile_name)
+ profiles_to_rename.push_back(GetPathOfProfileAtIndex(i));
+ }
+
+ // Rename the necessary profiles.
+ std::vector<base::FilePath>::const_iterator it;
+ for (it = profiles_to_rename.begin(); it != profiles_to_rename.end(); ++it) {
+ size_t profile_index = GetIndexOfProfileWithPath(*it);
+ SetProfileIsUsingDefaultNameAtIndex(profile_index, true);
+ // This will assign a new "Person %d" type name and re-sort the cache.
+ SetNameOfProfileAtIndex(profile_index, ChooseNameForNewProfile(
+ GetAvatarIconIndexOfProfileAtIndex(profile_index)));
+ }
+#endif
+}
void OnAvatarPictureSaved(const std::string& file_name,
const base::FilePath& profile_path);
+ // Migrate any legacy profile names ("First user", "Default Profile") to
+ // new style default names ("Person 1"), and download and high-res avatars
+ // used by the profiles.
+ void MigrateLegacyProfileNamesAndDownloadAvatars();
+
PrefService* prefs_;
std::vector<std::string> sorted_keys_;
base::FilePath user_data_dir_;
EXPECT_TRUE(base::DeleteFile(icon_path, true));
EXPECT_FALSE(base::PathExists(icon_path));
}
+
+TEST_F(ProfileInfoCacheTest, MigrateLegacyProfileNamesWithNewAvatarMenu) {
+ switches::EnableNewAvatarMenuForTesting(CommandLine::ForCurrentProcess());
+ EXPECT_EQ(0U, GetCache()->GetNumberOfProfiles());
+
+ base::FilePath path_1 = GetProfilePath("path_1");
+ GetCache()->AddProfileToCache(path_1, ASCIIToUTF16("Default Profile"),
+ base::string16(), 0, std::string());
+ base::FilePath path_2 = GetProfilePath("path_2");
+ GetCache()->AddProfileToCache(path_2, ASCIIToUTF16("First user"),
+ base::string16(), 1, std::string());
+ base::string16 name_3 = ASCIIToUTF16("Lemonade");
+ base::FilePath path_3 = GetProfilePath("path_3");
+ GetCache()->AddProfileToCache(path_3, name_3,
+ base::string16(), 2, std::string());
+ base::string16 name_4 = ASCIIToUTF16("Batman");
+ base::FilePath path_4 = GetProfilePath("path_4");
+ GetCache()->AddProfileToCache(path_4, name_4,
+ base::string16(), 3, std::string());
+ base::string16 name_5 = ASCIIToUTF16("Person 2");
+ base::FilePath path_5 = GetProfilePath("path_5");
+ GetCache()->AddProfileToCache(path_5, name_5,
+ base::string16(), 2, std::string());
+
+ EXPECT_EQ(5U, GetCache()->GetNumberOfProfiles());
+
+
+ ResetCache();
+
+ // Legacy profile names like "Default Profile" and "First user" should be
+ // migrated to "Person %n" type names.
+ EXPECT_EQ(ASCIIToUTF16("Person 1"), GetCache()->GetNameOfProfileAtIndex(
+ GetCache()->GetIndexOfProfileWithPath(path_1)));
+ EXPECT_EQ(ASCIIToUTF16("Person 3"), GetCache()->GetNameOfProfileAtIndex(
+ GetCache()->GetIndexOfProfileWithPath(path_2)));
+
+ // Other profile names should not be migrated even if they're the old
+ // default cartoon profile names.
+ EXPECT_EQ(name_3, GetCache()->GetNameOfProfileAtIndex(
+ GetCache()->GetIndexOfProfileWithPath(path_3)));
+ EXPECT_EQ(name_4, GetCache()->GetNameOfProfileAtIndex(
+ GetCache()->GetIndexOfProfileWithPath(path_4)));
+ EXPECT_EQ(name_5, GetCache()->GetNameOfProfileAtIndex(
+ GetCache()->GetIndexOfProfileWithPath(path_5)));
+}
#endif
+
+TEST_F(ProfileInfoCacheTest,
+ DontMigrateLegacyProfileNamesWithoutNewAvatarMenu) {
+ EXPECT_EQ(0U, GetCache()->GetNumberOfProfiles());
+
+ base::string16 name_1 = ASCIIToUTF16("Default Profile");
+ base::FilePath path_1 = GetProfilePath("path_1");
+ GetCache()->AddProfileToCache(path_1, name_1,
+ base::string16(), 0, std::string());
+ base::string16 name_2 = ASCIIToUTF16("First user");
+ base::FilePath path_2 = GetProfilePath("path_2");
+ GetCache()->AddProfileToCache(path_2, name_2,
+ base::string16(), 1, std::string());
+ base::string16 name_3 = ASCIIToUTF16("Lemonade");
+ base::FilePath path_3 = GetProfilePath("path_3");
+ GetCache()->AddProfileToCache(path_3, name_3,
+ base::string16(), 2, std::string());
+ base::string16 name_4 = ASCIIToUTF16("Batman");
+ base::FilePath path_4 = GetProfilePath("path_4");
+ GetCache()->AddProfileToCache(path_4, name_4,
+ base::string16(), 3, std::string());
+ EXPECT_EQ(4U, GetCache()->GetNumberOfProfiles());
+
+ ResetCache();
+
+ // Profile names should have been preserved.
+ EXPECT_EQ(name_1, GetCache()->GetNameOfProfileAtIndex(
+ GetCache()->GetIndexOfProfileWithPath(path_1)));
+ EXPECT_EQ(name_2, GetCache()->GetNameOfProfileAtIndex(
+ GetCache()->GetIndexOfProfileWithPath(path_2)));
+ EXPECT_EQ(name_3, GetCache()->GetNameOfProfileAtIndex(
+ GetCache()->GetIndexOfProfileWithPath(path_3)));
+ EXPECT_EQ(name_4, GetCache()->GetNameOfProfileAtIndex(
+ GetCache()->GetIndexOfProfileWithPath(path_4)));
+}
+
new_profile_name,
new_avatar_url,
std::string());
+
+ ProfileMetrics::LogProfileAddNewUser(
+ ProfileMetrics::ADD_NEW_USER_LAST_DELETED);
} else {
// On the Mac, the browser process is not killed when all browser windows
// are closed, so just in case we are deleting the active profile, and no
EXPECT_EQ(default_profile_name,
profiles::GetAvatarNameForProfile(profile1->GetPath()));
- // We should display the actual profile name for signed in profiles.
+ // For a signed in profile with a default name we still display
+ // IDS_SINGLE_PROFILE_DISPLAY_NAME.
cache.SetUserNameOfProfileAtIndex(0, ASCIIToUTF16("user@gmail.com"));
EXPECT_EQ(profile_name1, cache.GetNameOfProfileAtIndex(0));
- EXPECT_EQ(profile_name1,
+ EXPECT_EQ(default_profile_name,
profiles::GetAvatarNameForProfile(profile1->GetPath()));
+ // For a signed in profile with a non-default Gaia given name we display the
+ // Gaia given name.
+ cache.SetUserNameOfProfileAtIndex(0, ASCIIToUTF16("user@gmail.com"));
+ const base::string16 gaia_given_name(ASCIIToUTF16("given name"));
+ cache.SetGAIAGivenNameOfProfileAtIndex(0, gaia_given_name);
+ EXPECT_EQ(gaia_given_name, cache.GetNameOfProfileAtIndex(0));
+ EXPECT_EQ(gaia_given_name,
+ profiles::GetAvatarNameForProfile(profile1->GetPath()));
+
// Multiple profiles means displaying the actual profile names.
const base::string16 profile_name2 = cache.ChooseNameForNewProfile(1);
Profile* profile2 = AddProfileToCache(profile_manager,
"path_2", profile_name2);
- EXPECT_EQ(profile_name1,
+ EXPECT_EQ(gaia_given_name,
profiles::GetAvatarNameForProfile(profile1->GetPath()));
EXPECT_EQ(profile_name2,
profiles::GetAvatarNameForProfile(profile2->GetPath()));
ProfileManager::CreateCallback());
// Spin the message loop so that all the callbacks can finish running.
base::RunLoop().RunUntilIdle();
- EXPECT_EQ(profile_name1,
+ EXPECT_EQ(gaia_given_name,
profiles::GetAvatarNameForProfile(profile1->GetPath()));
}
#endif // !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
// Enum for counting the ways users were added.
enum ProfileAdd {
- ADD_NEW_USER_ICON = 0, // User adds new user from icon menu
- ADD_NEW_USER_MENU, // User adds new user from menu bar
- ADD_NEW_USER_DIALOG, // User adds new user from create-profile dialog
- ADD_NEW_USER_MANAGER, // User adds new user from User Manager
+ ADD_NEW_USER_ICON = 0, // User adds new user from icon menu
+ ADD_NEW_USER_MENU, // User adds new user from menu bar
+ ADD_NEW_USER_DIALOG, // User adds new user from create-profile dialog
+ ADD_NEW_USER_MANAGER, // User adds new user from User Manager
+ ADD_NEW_USER_LAST_DELETED, // Auto-created after deleting last user
NUM_PROFILE_ADD_METRICS
};
if (index == std::string::npos)
return l10n_util::GetStringUTF16(IDS_SINGLE_PROFILE_DISPLAY_NAME);
- // Using the --new-profile-management flag, there's a couple of rules
- // about what the avatar button displays. If there's a single, local
- // profile, with a default name (i.e. of the form Person %d), it should
- // display IDS_SINGLE_PROFILE_DISPLAY_NAME. If this is a signed in profile,
- // or the user has edited the profile name, or there are multiple profiles,
- // it will return the actual name of the profile.
+ // Using the --new-avatar-menu flag, there's a couple of rules about what
+ // the avatar button displays. If there's a single profile, with a default
+ // name (i.e. of the form Person %d) not manually set, it should display
+ // IDS_SINGLE_PROFILE_DISPLAY_NAME. Otherwise, it will return the actual
+ // name of the profile.
base::string16 profile_name = cache.GetNameOfProfileAtIndex(index);
- bool has_default_name = cache.ProfileIsUsingDefaultNameAtIndex(index);
+ bool has_default_name = cache.ProfileIsUsingDefaultNameAtIndex(index) &&
+ cache.IsDefaultProfileName(profile_name);
- if (cache.GetNumberOfProfiles() == 1 && has_default_name &&
- cache.GetUserNameOfProfileAtIndex(index).empty()) {
+ if (cache.GetNumberOfProfiles() == 1 && has_default_name)
display_name = l10n_util::GetStringUTF16(IDS_SINGLE_PROFILE_DISPLAY_NAME);
- } else {
+ else
display_name = profile_name;
- }
}
return display_name;
}
void UpdateProfileName(Profile* profile,
const base::string16& new_profile_name) {
- PrefService* pref_service = profile->GetPrefs();
ProfileInfoCache& cache =
g_browser_process->profile_manager()->GetProfileInfoCache();
+ size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath());
+ if (profile_index == std::string::npos)
+ return;
+
+ if (new_profile_name == cache.GetNameOfProfileAtIndex(profile_index))
+ return;
// This is only called when updating the profile name through the UI,
// so we can assume the user has done this on purpose.
- size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath());
- if (profile_index != std::string::npos)
- pref_service->SetBoolean(prefs::kProfileUsingDefaultName, false);
+ PrefService* pref_service = profile->GetPrefs();
+ pref_service->SetBoolean(prefs::kProfileUsingDefaultName, false);
// Updating the profile preference will cause the cache to be updated for
// this preference.
content::WebContents* new_contents) {
RetargetingDetails details;
details.source_web_contents = source_web_contents_;
- details.source_render_frame_id = GetRenderFrameHost()->GetRoutingID();
+ // Don't use GetRenderFrameHost() as it may be NULL. crbug.com/399789
+ details.source_render_frame_id = render_frame_id_;
details.target_url = url;
details.target_web_contents = new_contents;
details.not_yet_in_tabstrip = false;
return (keyEvent.shiftKey || (keyEvent.keyCode == 16));
break;
case 'metaKey':
- return (keyEvent.metaKey ||
- (!cvox.ChromeVox.isChromeOS && keyEvent.keyCode == 91));
+ return (keyEvent.metaKey || (keyEvent.keyCode == 91));
break;
case 'searchKeyHeld':
return ((cvox.ChromeVox.isChromeOS && keyEvent.keyCode == 91) ||
"externally_connectable": {
"ids": ["*"],
"matches": [
- "*://www.google.com/*/chrome/devices/goodies.html*"
+ "*://www.google.com/*chrome/devices/goodies.html*"
]
}
}
"permissions": [
"alarms",
"identity",
+ "identity.email",
"chromeosInfoPrivate",
+ "fileSystem",
"firstRunPrivate",
"management",
"metricsPrivate",
"oauth2": {
"client_id": "929143421683.apps.googleusercontent.com",
"scopes": [
- "https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/supportcontent",
"https://www.google.com/accounts/OAuthLogin"
]
],
"title": "Open Help Article"
},
+ "hts": {
+ "matches": [
+ "https://support.google.com/chromeos-gethelp/rts*",
+ "https://support.google.com/chromeos-gethelp/helpouts*"
+ ],
+ "title": "Contact Support"
+ },
"home": {
"matches": [
"https://support.google.com/chromeos-gethelp",
"productId": 512
},
{
+ "vendorId": 4176,
+ "productId": 275
+ },
+ {
+ "vendorId": 4176,
+ "productId": 277
+ },
+ {
+ "vendorId": 4176,
+ "productId": 288
+ },
+ {
+ "vendorId": 4176,
+ "productId": 1025
+ },
+ {
"vendorId": 9601,
"productId": 61904
},
};
/**
+ * @const
+ */
+UsbGnubbyDevice.WINUSB_VID_PIDS = [
+ {'vendorId': 4176, 'productId': 529} // Yubico WinUSB
+];
+
+/**
* @param {function(Array)} cb Enumerate callback
*/
UsbGnubbyDevice.enumerate = function(cb) {
- var permittedDevs;
var numEnumerated = 0;
var allDevs = [];
function enumerated(devs) {
allDevs = allDevs.concat(devs);
- if (++numEnumerated == permittedDevs.length) {
+ if (++numEnumerated == UsbGnubbyDevice.WINUSB_VID_PIDS.length) {
cb(allDevs);
}
}
- GnubbyDevice.getPermittedUsbDevices(function(devs) {
- permittedDevs = devs;
- for (var i = 0; i < devs.length; i++) {
- chrome.usb.getDevices(devs[i], enumerated);
- }
- });
+ for (var i = 0; i < UsbGnubbyDevice.WINUSB_VID_PIDS.length; i++) {
+ chrome.usb.getDevices(UsbGnubbyDevice.WINUSB_VID_PIDS[i], enumerated);
+ }
};
/**
"easyUnlockPrivate",
"systemPrivate",
"alarms",
- "gcm"
+ "gcm",
+ "system.display",
+ "chromeosInfoPrivate",
+ "tabs"
],
"app": {
* found in the LICENSE file. */
body {
- font-family: 'Noto Sans UI', 'Droid Sans Fallback', sans-serif;
+ font-family: 'Noto Sans UI', sans-serif;
font-size: 12px;
}
"default_locale": "en",
"manifest_version": 2,
"content_security_policy": "default-src 'self'; img-src 'self' data:;",
+ "background": {
+ "scripts": [
+ "js/background-bundle.js"
+ ],
+ "persistent": false
+ },
+ "content_scripts": [
+ {
+ "js": ["js/content_script_head.js"],
+ "matches": ["https://support.google.com/chromebook/*"],
+ "run_at": "document_start"
+ },
+ {
+ "js": ["js/content_script_foot.js"],
+ "matches": ["https://support.google.com/chromebook/*"],
+ "run_at": "document_idle"
+ }
+ ],
+ "permissions": [
+ "storage"
+ ],
"incognito": "split"
}
}
body.alternate-logo #logo {
- background-image: url('images/white_google_logo.png@2x');
+ -webkit-mask-image: url('images/google_logo.png@2x');
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-size: 100%;
+ background: #eee;
}
#fakebox {
font-size: 18px;
height: 36px;
line-height: 36px;
- max-width: 620px;
+ max-width: 672px;
position: relative;
/* #fakebox width (here and below) should be 2px less than #mv-tiles
to account for its border. */
color: #bbb;
font-family: arial, sans-serif;
font-size: 16px;
- height: 16px;
+ height: 100%;
left: 9px;
margin-top: 1px;
+ overflow: hidden;
position: absolute;
+ text-align: initial;
+ text-overflow: ellipsis;
vertical-align: middle;
- visibility: hidden;
+ visibility: inherit;
+ white-space: nowrap;
+ width: calc(100% - 2 * 9px);
}
body[dir=rtl] #fakebox-text {
body.fakebox-drag-focused #fakebox-text,
body.fakebox-focused #fakebox-text {
- visibility: inherit;
+ visibility: hidden;
}
body.fakebox-drag-focused #cursor {
}
.md #most-visited {
- margin-top: 50px;
+ margin-top: 64px;
}
#mv-tiles {
}
.md #mv-tiles {
- height: calc(2 * 126px);
- line-height: 126px;
+ height: calc(2 * 146px);
+ line-height: 146px;
}
.mv-tile {
}
.md .mv-tile {
- background: #f2f2f2;
+ background: rgb(242,242,242);
border-radius: 1px;
- height: 114px;
- margin-left: 6px;
- margin-right: 6px;
- width: 146px;
+ height: 130px;
+ margin-left: 8px;
+ margin-right: 8px;
+ width: 156px;
}
.md .mv-page-ready {
}
.md.dark .mv-tile {
- background: #333;
+ background: rgb(51,51,51);
}
.mv-tile-inner {
.md .mv-mask {
border-color: transparent;
border-radius: 2px;
- height: 112px;
- width: 144px;
+ height: calc(130px - 2px);
+ width: calc(156px - 2px);
}
/* Styling border. */
}
.default-theme.classical .mv-page-ready:hover .mv-mask,
-.default-theme.classical .mv-page-ready:focus .mv-mask {
+.default-theme.classical .mv-page-ready .mv-focused ~ .mv-mask {
border-color: #7f7f7f;
}
.default-theme.md.old-hover .mv-page-ready:hover .mv-mask,
-.default-theme.md.old-hover .mv-page-ready:focus .mv-mask {
+.default-theme.md.old-hover .mv-page-ready .mv-focused ~ .mv-mask {
border-color: #999;
}
.default-theme.md.dark .mv-page-ready:hover .mv-mask,
-.default-theme.md.dark .mv-page-ready:focus .mv-mask,
-.default-theme.md.old-hover.dark .mv-page-ready:hover .mv-mask {
+.default-theme.md.dark .mv-page-ready .mv-focused ~ .mv-mask,
+.default-theme.md.old-hover.dark .mv-page-ready:hover .mv-mask,
+.default-theme.md.old-hover.dark .mv-page-ready .mv-focused ~ .mv-mask {
border-color: #888;
}
/* Styling shadow. */
-.md .mv-page-ready .mv-mask {
+.default-theme.md .mv-page-ready .mv-mask {
-webkit-transition: box-shadow 200ms, border 200ms;
}
-.default-theme.md .mv-page-ready:hover .mv-mask {
- box-shadow: 0 2px 8px rgba(0,0,0,0.3);
+
+.default-theme.md .mv-page-ready:hover .mv-mask,
+.default-theme.md .mv-page-ready .mv-focused ~ .mv-mask {
+ box-shadow: 0 1px 2px 0 rgba(0,0,0,0.1), 0 4px 8px 0 rgba(0,0,0,0.2);
}
-.default-theme..md.dark .mv-page-ready:hover .mv-mask,
-.default-theme..md.old-hover .mv-page-ready:hover .mv-mask {
+.default-theme.md.dark .mv-page-ready:hover .mv-mask,
+.default-theme.md.old-hover .mv-page-ready:hover .mv-mask {
box-shadow: none;
}
/* Styling background. */
-.classical .mv-page:focus .mv-mask {
+.classical .mv-page .mv-focused ~ .mv-mask {
-webkit-transition: background-color 100ms ease-in-out;
background: linear-gradient(rgba(255, 255, 255, 0),
rgba(255, 255, 255, 0) 80%, rgba(255, 255, 255, 0.9));
background-color: rgba(0, 0, 0, 0.35);
}
-.md .mv-page:focus .mv-mask {
+.md .mv-page .mv-focused ~ .mv-mask {
-webkit-transition: box-shadow 200ms, border 200ms,
- background-color 100ms ease-in-out, ;
+ background-color 100ms ease-in-out;
background: rgba(0, 0, 0, 0.3);
border-color: rgba(0, 0, 0, 0.3);
}
.md .mv-title {
bottom: auto;
height: 15px;
- left: 28px;
- top: 7px;
- width: 112px;
+ left: 32px;
+ top: 9px;
+ width: calc(156px - 32px - 4px);
}
@media (-webkit-min-device-pixel-ratio: 2) {
.md .mv-title {
- top: 6px;
+ top: 8px;
}
}
body[dir=rtl] .md .mv-title {
left: auto;
- right: 28px;
+ right: 32px;
}
.mv-thumb {
.md .mv-thumb,
.md .mv-thumb-fallback {
border-radius: 0;
- height: 82px;
+ height: 94px;
left: 4px;
- top: 28px;
- width: 138px;
+ top: 32px;
+ width: 148px;
}
body[dir=rtl] .md .mv-thumb,
}
.md .mv-thumb-fallback {
- background: #fff;
- padding: none;
+ background-color: #fff;
position: absolute;
}
.md.dark .mv-thumb-fallback {
- background: #555;
+ background-color: #555;
}
.md .mv-thumb-fallback .dot {
- background: #f2f2f2;
- border-radius: 16px;
+ background-color: #f2f2f2;
+ border-radius: 8px;
display: block;
- height: 32px;
+ height: 16px;
left: 50%;
- margin-left: -16px;
- margin-top: -16px;
+ margin-left: -8px;
+ margin-top: -8px;
position: absolute;
top: 50%;
- width: 32px;
+ width: 16px;
}
.md.dark .mv-thumb-fallback .dot {
- background: #333;
+ background-color: #333;
}
.mv-x-hide .mv-x {
/* An X button to blacklist a tile or hide the notification. */
.mv-x {
background-color: transparent;
- background-image: url(images/close_2.png);
border: none;
- cursor: default;
- height: 16px;
- width: 16px;
+ cursor: pointer;
}
.mv-page .mv-x {
position: absolute;
}
-.mv-x:hover,
-#mv-notice-x:focus {
- background-image: url(images/close_2_hover.png);
+.classical .mv-x {
+ background-image: url('images/close_2.png');
+ height: 16px;
+ width: 16px;
+}
+
+.classical .mv-x:hover,
+.classical #mv-notice-x:focus {
+ background-image: url('images/close_2_hover.png');
}
-.mv-x:active {
- background-image: url(images/close_2_active.png);
+.classical .mv-x:active,
+.classical #mv-notice-x:active {
+ background-image: url('images/close_2_active.png');
}
.classical .mv-page .mv-x {
top: 2px;
}
+body[dir=rtl] .classical .mv-page .mv-x {
+ left: 2px;
+ right: auto;
+}
+
+#mv-notice-x {
+ display: inline-block;
+ position: relative;
+}
+
+.md #mv-notice-x {
+ -webkit-transform: translate(0,-8px);
+}
+
.md .mv-x {
- background-color: rgba(187,187,187,0.8);
- border-radius: 8px;
+ height: 32px;
+ width: 32px;
}
-.md.dark .mv-x {
- background-color: rgba(119,119,119,0.8);
+.md .mv-x .mv-x-inner {
+ -webkit-mask-image: -webkit-image-set(
+ url('images/close_3_mask.png') 1x,
+ url('images/close_3_mask.png@2x') 2x);
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-size: 10px 10px;
+ background-color: rgba(90,90,90,0.7);
+ height: 10px;
+ left: 50%;
+ margin-left: -5px;
+ margin-top: -5px;
+ position: absolute;
+ top: 50%;
+ width: 10px;
}
-.md .mv-page .mv-x {
- right: 4px;
- top: 5px;
+.md.dark .mv-x .mv-x-inner {
+ background-color: rgba(255,255,255,0.7);
}
-body[dir=rtl] .classical .mv-page .mv-x {
- left: 2px;
- right: auto;
+.md .mv-x:hover .mv-x-inner,
+.md #mv-notice-x:focus .mv-x-inner {
+ background-color: rgb(90,90,90);
+}
+
+.md.dark .mv-x:hover .mv-x-inner,
+.md.dark #mv-notice-x:focus .mv-x-inner {
+ background-color: rgb(255,255,255);
+}
+
+.md .mv-x:active .mv-x-inner,
+.md #mv-notice-x:active .mv-x-inner {
+ background-color: rgb(66,133,244);
+}
+
+.md.dark .mv-x:active .mv-x-inner,
+.md.dark #mv-notice-x:active .mv-x-inner {
+ background-color: rgba(255,255,255,0.5);
+}
+
+.md .mv-page .mv-x {
+ /* background color needs to match .md .mv-tile */
+ background: linear-gradient(to right, transparent, rgb(242,242,242) 10%);
+ right: 0;
+ top: 0;
}
body[dir=rtl] .md .mv-page .mv-x {
- left: 4px;
+ /* background color needs to match .md .mv-tile */
+ background: linear-gradient(to left, transparent, rgb(242,242,242) 10%);
+ left: 0;
right: auto;
}
+.md.dark .mv-page .mv-x {
+ /* background color needs to match .md.dark .mv-tile */
+ background: linear-gradient(to right, transparent, rgba(51,51,51,0.9) 30%);
+}
+
+body[dir=rtl] .md.dark .mv-page .mv-x {
+ /* background color needs to match .md.dark .mv-tile */
+ background: linear-gradient(to left, transparent, rgba(51,51,51,0.9) 30%);
+}
+
.mv-page-ready:hover .mv-x {
-webkit-transition-delay: 500ms;
opacity: 1;
}
.md .mv-favicon {
- left: 6px;
- top: 6px;
+ left: 8px;
+ top: 8px;
}
body[dir=rtl] .md .mv-favicon {
left: auto;
- right: 6px;
- top: 6px;
+ right: 8px;
+ top: 8px;
+}
+
+.md .mv-favicon-fallback {
+ background-image: -webkit-image-set(
+ url('images/ntp_default_favicon.png') 1x,
+ url('images/ntp_default_favicon.png@2x') 2x);
+ background-repeat: no-repeat;
+ background-size: 16px 16px;
}
/* The notification shown when a tile is blacklisted. */
#mv-notice span {
cursor: default;
+ display: inline-block;
+ height: 16px;
+ line-height: 16px;
+ vertical-align: top;
}
/* Links in the notification. */
color: #fff;
}
+.default-theme.dark #mv-notice-links span {
+ color: #fff;
+}
+
#mv-notice-links .mv-x {
-webkit-margin-start: 8px;
outline: none;
<span id="mv-notice-links">
<span id="mv-undo" tabIndex="1"></span>
<span id="mv-restore" tabIndex="1"></span>
- <button id="mv-notice-x" tabIndex="1" class="mv-x"></button>
+ <div id="mv-notice-x" tabIndex="1" class="mv-x"></div>
</span>
</div>
</div>
ALTERNATE_LOGO: 'alternate-logo', // Shows white logo if required by theme
BLACKLIST: 'mv-blacklist', // triggers tile blacklist animation
BLACKLIST_BUTTON: 'mv-x',
+ BLACKLIST_BUTTON_INNER: 'mv-x-inner',
DARK: 'dark',
DEFAULT_THEME: 'default-theme',
DELAYED_HIDE_NOTIFICATION: 'mv-notice-delayed-hide',
FAKEBOX_DRAG_FOCUS: 'fakebox-drag-focused',
FAVICON: 'mv-favicon',
FAVICON_FALLBACK: 'mv-favicon-fallback',
+ FOCUSED: 'mv-focused',
HIDE_BLACKLIST_BUTTON: 'mv-x-hide', // hides blacklist button during animation
HIDE_FAKEBOX_AND_LOGO: 'hide-fakebox-logo',
HIDE_NOTIFICATION: 'mv-notice-hide',
* @const
*/
var KEYCODE = {
- DELETE: 46,
ENTER: 13
};
/**
- * True if a page has been blacklisted and we're waiting on the
- * onmostvisitedchange callback. See onMostVisitedChange() for how this
- * is used.
- * @type {boolean}
+ * The iframe element which is currently keyboard focused, or null.
+ * @type {?Element}
*/
-var isBlacklisting = false;
+var focusedIframe = null;
/**
- * Stores whether the current theme has a dark background.
+ * True if a page has been blacklisted and we're waiting on the
+ * onmostvisitedchange callback. See onMostVisitedChange() for how this
+ * is used.
* @type {boolean}
*/
-var isBackgroundDark = false;
+var isBlacklisting = false;
/**
/**
+ * The color of the title in RRGGBBAA format.
+ * @type {?string}
+ */
+var titleColor = null;
+
+
+/**
* Hide most visited tiles for at most this many milliseconds while painting.
* @type {number}
* @const
/**
- * Determines whether a theme should be considered to have dark background.
- * @param {ThemeBackgroundInfo} info Theme background information.
- * @return {boolean} Whether the theme has dark background.
+ * Heuristic to determine whether a theme should be considered to be dark, so
+ * the colors of various UI elements can be adjusted.
+ * @param {ThemeBackgroundInfo|undefined} info Theme background information.
+ * @return {boolean} Whether the theme is dark.
* @private
*/
-function getIsBackgroundDark(info) {
- if (info.imageUrl)
- return true;
- var rgba = info.backgroundColorRgba;
+function getIsThemeDark(info) {
+ if (!info)
+ return false;
+ // Heuristic: light text implies dark theme.
+ var rgba = info.textColorRgba;
var luminance = 0.3 * rgba[0] + 0.59 * rgba[1] + 0.11 * rgba[2];
- return luminance < 128;
+ return luminance >= 128;
}
* @private
*/
function renderTheme() {
+ var fakeboxText = $(IDS.FAKEBOX_TEXT);
+ if (fakeboxText) {
+ fakeboxText.innerHTML = '';
+ if (NTP_DESIGN.showFakeboxHint &&
+ configData.translatedStrings.searchboxPlaceholder) {
+ fakeboxText.textContent =
+ configData.translatedStrings.searchboxPlaceholder;
+ }
+ }
+
var info = ntpApiHandle.themeBackgroundInfo;
+ var isThemeDark = getIsThemeDark(info);
+ ntpContents.classList.toggle(CLASSES.DARK, isThemeDark);
if (!info) {
- isBackgroundDark = false;
+ titleColor = NTP_DESIGN.titleColor;
return;
}
- isBackgroundDark = getIsBackgroundDark(info);
- ntpContents.classList.toggle(CLASSES.DARK, isBackgroundDark);
+ if (!info.usingDefaultTheme && info.textColorRgba) {
+ titleColor = convertToRRGGBBAAColor(info.textColorRgba);
+ } else {
+ titleColor = isThemeDark ?
+ NTP_DESIGN.titleColorAgainstDark : NTP_DESIGN.titleColor;
+ }
var background = [convertToRGBAColor(info.backgroundColorRgba),
info.imageUrl,
info.imageTiling,
info.imageHorizontalAlignment,
info.imageVerticalAlignment].join(' ').trim();
+
document.body.style.background = background;
document.body.classList.toggle(CLASSES.ALTERNATE_LOGO, info.alternateLogo);
updateThemeAttribution(info.attributionUrl);
function setCustomThemeStyle(opt_themeInfo) {
var customStyleElement = $(IDS.CUSTOM_THEME_STYLE);
var head = document.head;
-
if (opt_themeInfo && !opt_themeInfo.usingDefaultTheme) {
ntpContents.classList.remove(CLASSES.DEFAULT_THEME);
var themeStyle =
' border: 1px solid ' +
convertToRGBAColor(opt_themeInfo.sectionBorderColorRgba) + ';' +
'}' +
- '.mv-page-ready:hover .mv-mask, .mv-page-ready:focus .mv-mask {' +
+ '.mv-page-ready:hover .mv-mask, .mv-page-ready .mv-focused ~ .mv-mask {' +
' border-color: ' +
convertToRGBAColor(opt_themeInfo.headerColorRgba) + ';' +
'}';
/**
+ * Converts an Array of color components into RRGGBBAA format.
+ * @param {Array.<number>} color Array of rgba color components.
+ * @return {string} Color string in RRGGBBAA format.
+ * @private
+ */
+function convertToRRGGBBAAColor(color) {
+ return color.map(function(t) {
+ return ('0' + t.toString(16)).slice(-2); // To 2-digit, 0-padded hex.
+ }).join('');
+}
+
+
+ /**
* Converts an Array of color components into RGBA format "rgba(R,G,B,A)".
* @param {Array.<number>} color Array of rgba color components.
* @return {string} CSS color in RGBA format.
function getMostVisitedTitleIframeUrl(rid, position) {
var url = 'chrome-search://most-visited/' +
encodeURIComponent(MOST_VISITED_TITLE_IFRAME);
- var titleColor = isBackgroundDark ? NTP_DESIGN.titleColorAgainstDark :
- NTP_DESIGN.titleColor;
var params = [
'rid=' + encodeURIComponent(rid),
'f=' + encodeURIComponent(NTP_DESIGN.fontFamily),
function createTile(page, position) {
var tileElem = document.createElement('div');
tileElem.classList.add(CLASSES.TILE);
+ // Prevent tile from being selected (and highlighted) when areas outside the
+ // <iframe>s are clicked.
+ tileElem.addEventListener('mousedown', function(e) {
+ e.preventDefault();
+ });
var innerElem = createAndAppendElement(tileElem, 'div', CLASSES.TILE_INNER);
if (page) {
// The click handler for navigating to the page identified by the RID.
tileElem.addEventListener('click', navigateFunction);
- // Make thumbnails tab-accessible.
- tileElem.setAttribute('tabindex', '1');
- registerKeyHandler(tileElem, KEYCODE.ENTER, navigateFunction);
-
// The iframe which renders the page title.
var titleElem = document.createElement('iframe');
- titleElem.tabIndex = '-1';
+ // Enable tab navigation on the iframe, which will move the selection to the
+ // link element (which also has a tabindex).
+ titleElem.tabIndex = '0';
// Why iframes have IDs:
//
// The iframe which renders either a thumbnail or domain element.
var thumbnailElem = document.createElement('iframe');
thumbnailElem.tabIndex = '-1';
+ thumbnailElem.setAttribute('aria-hidden', 'true');
// Keep this ID here. See comment above.
thumbnailElem.id = 'thumb-' + rid;
thumbnailElem.className = CLASSES.THUMBNAIL;
// The button used to blacklist this page.
var blacklistButton = createAndAppendElement(
innerElem, 'div', CLASSES.BLACKLIST_BUTTON);
+ createAndAppendElement(
+ blacklistButton, 'div', CLASSES.BLACKLIST_BUTTON_INNER);
var blacklistFunction = generateBlacklistFunction(rid);
blacklistButton.addEventListener('click', blacklistFunction);
blacklistButton.title = configData.translatedStrings.removeThumbnailTooltip;
var maskElement = createAndAppendElement(
innerElem, 'div', CLASSES.THUMBNAIL_MASK);
- // When a tile is focused, have delete also blacklist the page.
- registerKeyHandler(tileElem, KEYCODE.DELETE, blacklistFunction);
-
// The page favicon, or a fallback.
var favicon = createAndAppendElement(innerElem, 'div', CLASSES.FAVICON);
if (page.faviconUrl) {
* Generates a function to be called when the page with the corresponding RID
* is blacklisted.
* @param {number} rid The RID of the page being blacklisted.
- * @return {function(Event)} A function which handles the blacklisting of the
+ * @return {function(Event=)} A function which handles the blacklisting of the
* page by updating state variables and notifying Chrome.
*/
function generateBlacklistFunction(rid) {
return function(e) {
// Prevent navigation when the page is being blacklisted.
- e.stopPropagation();
+ if (e)
+ e.stopPropagation();
userInitiatedMostVisitedChange = true;
isBlacklisting = true;
*/
function hideNotification() {
notification.classList.add(CLASSES.HIDE_NOTIFICATION);
+ notification.classList.remove(CLASSES.DELAYED_HIDE_NOTIFICATION);
}
/**
- * Resizes elements because the number of tile columns may need to change in
- * response to resizing. Also shows or hides extra tiles tiles according to the
- * new width of the page.
+ * Recomputes the number of tile columns, and width of various contents based
+ * on the width of the window.
+ * @return {boolean} Whether the number of tile columns has changed.
*/
-function onResize() {
+function updateContentWidth() {
var tileRequiredWidth = NTP_DESIGN.tileWidth + NTP_DESIGN.tileMargin;
// If innerWidth is zero, then use the maximum snap size.
var maxSnapSize = MAX_NUM_COLUMNS * tileRequiredWidth -
else if (newNumColumns > MAX_NUM_COLUMNS)
newNumColumns = MAX_NUM_COLUMNS;
- if (numColumnsShown != newNumColumns) {
- numColumnsShown = newNumColumns;
- var tilesContainerWidth = numColumnsShown * tileRequiredWidth;
- tilesContainer.style.width = tilesContainerWidth + 'px';
- if (fakebox) {
- fakebox.style.width = // -2 to account for border.
- (tilesContainerWidth - NTP_DESIGN.tileMargin - 2) + 'px';
- }
+ if (numColumnsShown === newNumColumns)
+ return false;
+
+ numColumnsShown = newNumColumns;
+ var tilesContainerWidth = numColumnsShown * tileRequiredWidth;
+ tilesContainer.style.width = tilesContainerWidth + 'px';
+ if (fakebox) {
+ // -2 to account for border.
+ var fakeboxWidth = (tilesContainerWidth - NTP_DESIGN.tileMargin - 2);
+ fakebox.style.width = fakeboxWidth + 'px';
+ }
+ return true;
+}
+
+
+/**
+ * Resizes elements because the number of tile columns may need to change in
+ * response to resizing. Also shows or hides extra tiles tiles according to the
+ * new width of the page.
+ */
+function onResize() {
+ if (updateContentWidth()) {
// Render without clearing tiles.
renderAndShowTiles();
}
/**
+ * Event handler for the focus changed and blacklist messages on link elements.
+ * Used to toggle visual treatment on the tiles (depending on the message).
+ * @param {Event} event Event received.
+ */
+function handlePostMessage(event) {
+ if (event.origin !== 'chrome-search://most-visited')
+ return;
+
+ if (event.data === 'linkFocused') {
+ var activeElement = document.activeElement;
+ if (activeElement.classList.contains(CLASSES.TITLE)) {
+ activeElement.classList.add(CLASSES.FOCUSED);
+ focusedIframe = activeElement;
+ }
+ } else if (event.data === 'linkBlurred') {
+ if (focusedIframe)
+ focusedIframe.classList.remove(CLASSES.FOCUSED);
+ focusedIframe = null;
+ } else if (event.data.indexOf('tileBlacklisted') === 0) {
+ var tilePosition = event.data.split(',')[1];
+ if (tilePosition)
+ generateBlacklistFunction(tiles[parseInt(tilePosition, 10)].rid)();
+ }
+}
+
+
+/**
* Prepares the New Tab Page by adding listeners, rendering the current
* theme, the most visited pages section, and Google-specific elements for a
* Google-provided page.
var fakeboxHtml = [];
fakeboxHtml.push('<input id="' + IDS.FAKEBOX_INPUT +
'" autocomplete="off" tabindex="-1" aria-hidden="true">');
- if (NTP_DESIGN.showFakeboxHint &&
- configData.translatedStrings.searchboxPlaceholder) {
- fakeboxHtml.push('<div id="' + IDS.FAKEBOX_TEXT + '">' +
- configData.translatedStrings.searchboxPlaceholder + '</div>');
- }
+ fakeboxHtml.push('<div id="' + IDS.FAKEBOX_TEXT + '"></div>');
fakeboxHtml.push('<div id="cursor"></div>');
fakebox.innerHTML = fakeboxHtml.join('');
document.body.classList.add(CLASSES.NON_GOOGLE_PAGE);
}
+ // Hide notifications after fade out, so we can't focus on links via keyboard.
+ notification.addEventListener('webkitTransitionEnd', hideNotification);
+
var notificationMessage = $(IDS.NOTIFICATION_MESSAGE);
notificationMessage.textContent =
configData.translatedStrings.thumbnailRemovedNotification;
configData.translatedStrings.attributionIntro;
var notificationCloseButton = $(IDS.NOTIFICATION_CLOSE_BUTTON);
+ createAndAppendElement(
+ notificationCloseButton, 'div', CLASSES.BLACKLIST_BUTTON_INNER);
notificationCloseButton.addEventListener('click', hideNotification);
window.addEventListener('resize', onResize);
- onResize();
+ updateContentWidth();
var topLevelHandle = getEmbeddedSearchApiHandle();
document.body.classList.add(CLASSES.RTL);
$(IDS.TILES).dir = 'rtl';
}
+
+ window.addEventListener('message', handlePostMessage);
}
* fontSize: Font size to use for the <iframe>s, in px.
* tileWidth: The width of each suggestion tile, in px.
* tileMargin: Spacing between successive tiles, in px.
- * titleColor: The RRGGBB color of title text.
- * titleColorAgainstDark: The RRGGBB color of title text against a dark theme.
+ * titleColor: The RRGGBBAA color of title text.
+ * titleColorAgainstDark: The RRGGBBAA color of title text against a dark theme.
* titleTextAlign: (Optional) The alignment of title text. If unspecified, the
* default value is 'center'.
* titleTextFade: (Optional) The number of pixels beyond which title
* text begins to fade. This overrides the default ellipsis style.
- * thumbnailTextColor: The RRGGBB color that thumbnail <iframe> may use to
+ * thumbnailTextColor: The RRGGBBAA color that thumbnail <iframe> may use to
* display text message in place of missing thumbnail.
* thumbnailFallback: (Optional) A value in THUMBNAIL_FALLBACK to specify the
* thumbnail fallback strategy. If unassigned, then the thumbnail.html
name: opt_name,
fontFamily: 'arial, sans-serif',
fontSize: 12,
- tileWidth: 146,
- tileMargin: 12,
- titleColor: '000000',
- titleColorAgainstDark: 'd2d2d2',
+ tileWidth: 156,
+ tileMargin: 16,
+ titleColor: '323232ff',
+ titleColorAgainstDark: 'd2d2d2ff',
titleTextAlign: 'inherit',
- titleTextFade: 112 - 24, // 112px wide title with 24 pixel fade at end.
- thumbnailTextColor: '777777',
+ titleTextFade: 122 - 36, // 112px wide title with 32 pixel fade at end.
+ thumbnailTextColor: '323232ff', // Unused.
thumbnailFallback: THUMBNAIL_FALLBACK.DOT,
showFakeboxHint: true
};
fontSize: 11,
tileWidth: 140,
tileMargin: 20,
- titleColor: '777777',
- titleColorAgainstDark: '777777',
+ titleColor: '777777ff',
+ titleColorAgainstDark: '777777ff',
titleTextAlign: 'center',
titleTextFade: null, // Default to ellipsis.
- thumbnailTextColor: '777777',
+ thumbnailTextColor: '777777ff',
thumbnailFallback: null, // Default to false.
showFakeboxHint: false
};
a {
height: 100%;
+ position: relative;
width: 100%;
}
width: 90%;
}
+span.blocker {
+ display: inline-block;
+ height: 100%;
+ position: absolute;
+ width: 100%;
+}
+
img {
height: 100%;
width: 100%;
var link = createMostVisitedLink(
params, data.url, data.title, undefined, data.direction,
data.provider);
+ // Use blocker to prevent context menu from showing image-related items.
+ var blocker = document.createElement('span');
+ blocker.className = 'blocker';
+ link.appendChild(blocker);
link.appendChild(image);
displayLink(link);
};
link.href = href;
link.title = title;
link.target = '_top';
- // Exclude links from the tab order. The tabIndex is added to the thumbnail
- // parent container instead.
- link.tabIndex = '-1';
+ // Include links in the tab order. The tabIndex is necessary for
+ // accessibility.
+ link.tabIndex = '0';
if (text)
link.textContent = text;
link.addEventListener('mouseover', function() {
var ntpApiHandle = chrome.embeddedSearch.newTabPage;
ntpApiHandle.logEvent(NTP_LOGGING_EVENT_TYPE.NTP_MOUSEOVER);
});
+ link.addEventListener('focus', function() {
+ window.parent.postMessage('linkFocused', DOMAIN_ORIGIN);
+ });
+ link.addEventListener('blur', function() {
+ window.parent.postMessage('linkBlurred', DOMAIN_ORIGIN);
+ });
// Webkit's security policy prevents some Most Visited thumbnails from
// working (those with schemes different from http and https). Therefore,
// navigateContentWindow is being used in order to get all schemes working.
- link.addEventListener('click', function handleNavigation(e) {
+ var navigateFunction = function handleNavigation(e) {
var isServerSuggestion = 'url' in params;
// Ping are only populated for server-side suggestions, never for MV.
ntpApiHandle.navigateContentWindow(href, getDispositionFromEvent(e));
}
// Else follow <a> normally, so transition type would be LINK.
+ };
+
+ link.addEventListener('click', navigateFunction);
+ link.addEventListener('keydown', function(event) {
+ if (event.keyCode == 46 /* DELETE */ ||
+ event.keyCode == 8 /* BACKSPACE */) {
+ event.preventDefault();
+ window.parent.postMessage('tileBlacklisted,' + params.pos, DOMAIN_ORIGIN);
+ } else if (event.keyCode == 13 /* ENTER */ ||
+ event.keyCode == 32 /* SPACE */) {
+ navigateFunction(event);
+ }
});
return link;
/**
+ * Returns the color to display string with, depending on whether title is
+ * displayed, the current theme, and URL parameters.
+ * @param {Object.<string, string>} params URL parameters specifying style.
+ * @param {boolean} isTitle if the style is for the Most Visited Title.
+ * @return {string} The color to use, in "rgba(#,#,#,#)" format.
+ */
+function getTextColor(params, isTitle) {
+ // 'RRGGBBAA' color format overrides everything.
+ if ('c' in params && params.c.match(/^[0-9A-Fa-f]{8}$/)) {
+ // Extract the 4 pairs of hex digits, map to number, then form rgba().
+ var t = params.c.match(/(..)(..)(..)(..)/).slice(1).map(function(s) {
+ return parseInt(s, 16);
+ });
+ return 'rgba(' + t[0] + ',' + t[1] + ',' + t[2] + ',' + t[3] / 255 + ')';
+ }
+
+ // For backward compatibility with server-side NTP, look at themes directly
+ // and use param.c for non-title or as fallback.
+ var apiHandle = chrome.embeddedSearch.newTabPage;
+ var themeInfo = apiHandle.themeBackgroundInfo;
+ var c = '#777';
+ if (isTitle && themeInfo && !themeInfo.usingDefaultTheme) {
+ // Read from theme directly
+ c = convertArrayToRGBAColor(themeInfo.textColorRgba) || c;
+ } else if ('c' in params) {
+ c = convertToHexColor(parseInt(params.c, 16)) || c;
+ }
+ return c;
+}
+
+
+/**
* Decodes most visited styles from URL parameters.
* - c: A hexadecimal number interpreted as a hex color code.
* - f: font-family.
*/
function getMostVisitedStyles(params, isTitle) {
var styles = {
- color: '#777',
+ color: getTextColor(params, isTitle), // Handles 'c' in params.
fontFamily: '',
fontSize: 11
};
- var apiHandle = chrome.embeddedSearch.newTabPage;
- var themeInfo = apiHandle.themeBackgroundInfo;
- if (isTitle && themeInfo && !themeInfo.usingDefaultTheme) {
- styles.color = convertArrayToRGBAColor(themeInfo.textColorRgba) ||
- styles.color;
- } else if ('c' in params) {
- styles.color = convertToHexColor(parseInt(params.c, 16)) || styles.color;
- }
if ('f' in params && /^[-0-9a-zA-Z ,]+$/.test(params.f))
styles.fontFamily = params.f;
if ('fs' in params && isFinite(parseInt(params.fs, 10)))
-webkit-margin-start: 0.6em;
}
-div[guestmode=true] #appearance-section,
-div[guestmode=true] #startup-section,
-div[guestmode=true] #searchBox,
-div[guestmode=true] #reset-profile-settings-section {
+div[guestmode=true] :-webkit-any(
+<if expr="not chromeos">
+ #searchBox,
+</if>
+ #appearance-section,
+ #startup-section,
+ #reset-profile-settings-section) {
display: none;
}
<section>
<h3 id="voice-section-title" i18n-content="sectionTitleVoice" hidden></h3>
<div id="voice-section-content">
- <div id="hotword-search" hidden>
- <div class="checkbox">
- <span class="controlled-setting-with-label">
- <input id="hotword-search-enable" pref="hotword.search_enabled_2"
- metric="Options_HotwordCheckbox" type="checkbox" dialog-pref>
- <span>
- <label for="hotword-search-enable"
- i18n-values=".innerHTML:hotwordSearchEnable">
- </label>
- <span id="hotword-search-setting-indicator"
- pref="hotword.search_enabled_2" dialog-pref>
- </span>
- </span>
- </span>
- </div>
- </div>
<div id="hotword-always-on-search" hidden>
<div class="checkbox">
<span class="controlled-setting-with-label">
</span>
</div>
</if>
+ <div id="hotword-search" hidden>
+ <div class="checkbox">
+ <span class="controlled-setting-with-label">
+ <input id="hotword-search-enable" pref="hotword.search_enabled_2"
+ metric="Options_HotwordCheckbox" type="checkbox" dialog-pref>
+ <span>
+ <label for="hotword-search-enable"
+ i18n-values=".innerHTML:hotwordSearchEnable">
+ </label>
+ <span id="hotword-search-setting-indicator"
+ pref="hotword.search_enabled_2" dialog-pref>
+ </span>
+ </span>
+ </span>
+ </div>
+ </div>
</div>
</section>
<if expr="chromeos">
</div>
</section>
</if>
- <section id="reset-profile-settings-section" hidden>
+ <section id="reset-profile-settings-section">
<h3 i18n-content="resetProfileSettingsSectionTitle"></h3>
<div>
<span class="settings-row" i18n-content="resetProfileSettingsDescription">
* @private
*/
showHotwordSection_: function(opt_enabled, opt_error) {
- $('voice-section-title').hidden = false;
$('hotword-search').hidden = false;
$('hotword-search-setting-indicator').setError(opt_error);
if (opt_enabled && opt_error)
* @private
*/
showHotwordAlwaysOnSection_: function() {
+ $('voice-section-title').hidden = false;
$('hotword-always-on-search').hidden = false;
$('audio-logging').hidden = false;
},
'prl-version',
getActiveDictionaryValue(data, 'Cellular', 'PRLVersion'));
- var family = getActiveDictionaryValue(data, 'Cellular', 'GSM');
+ var family = getActiveDictionaryValue(data, 'Cellular', 'Family');
detailsPage.gsm = family == 'GSM';
if (detailsPage.gsm) {
$('iccid').textContent =
return;
this.expanded_ = expanded;
if (expanded) {
+ this.classList.add('show-items');
var oldExpanded = this.list.expandedItem;
this.list.expandedItem = this;
this.updateItems_();
if (oldExpanded)
oldExpanded.expanded = false;
- this.classList.add('show-items');
} else {
if (this.list.expandedItem == this) {
this.list.expandedItem = null;
div.className = 'cookie-item';
// Help out screen readers and such: this is a clickable thing.
div.setAttribute('role', 'button');
- div.tabIndex = 0;
div.textContent = text;
var apps = this.data.appsProtectingThis;
if (apps)
-webkit-margin-start: 14em;
-webkit-padding-start: 7px;
-webkit-transition: 150ms ease-in-out;
+ display: none;
height: 0;
opacity: 0;
/* Make the cookie items wrap correctly. */
}
.show-items .cookie-items {
+ display: block;
opacity: 1;
}
{
- "x-version": 12,
+ "x-version": 13,
"google-talk": {
"mime_types": [
],
],
"versions": [
{
- "version": "10.1.8",
+ "version": "10.1.11",
"status": "requires_authorization",
- "reference": "https://www.adobe.com/support/security/bulletins/apsb13-22.html"
+ "reference": "https://helpx.adobe.com/security/products/reader/apsb14-19.html"
},
{
"version": "11",
"status": "out_of_date"
},
{
- "version": "11.0.5",
+ "version": "11.0.8",
"status": "requires_authorization",
- "reference": "https://www.adobe.com/support/security/bulletins/apsb13-25.html"
+ "reference": "https://helpx.adobe.com/security/products/reader/apsb14-19.html"
}
],
"lang": "en-US",
"name": "Adobe Reader",
"help_url": "https://support.google.com/chrome/?p=plugin_pdf",
- "url": "http://www.adobe.com/support/downloads/detail.jsp?ftpID=5674",
+ "url": "https://get.adobe.com/reader/",
+ "displayurl": true,
"group_name_matcher": "*Adobe Acrobat*"
},
"apple-quicktime": {
Page.prototype.initializePage.call(this);
var self = this;
+
+ // If 'profilesInfo' doesn't exist, it's forbidden to delete profile.
+ // So don't display the delete-profile checkbox.
+ if (!loadTimeData.valueExists('profilesInfo') &&
+ $('sync-setup-delete-profile')) {
+ $('sync-setup-delete-profile').hidden = true;
+ }
+
$('basic-encryption-option').onchange =
$('full-encryption-option').onchange = function() {
self.onEncryptionRadioChanged_();
.pod {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
height: 226px;
+ /* On non-retina desktop, the text is blurry if we use the scale3d()
+ inherited from user_pod_row.js */
+ transform: scale(0.9);
+}
+
+podrow[ncolumns='6'] .pod {
+ transform: scale(0.8);
}
.pod.faded {
.pod.hovered:not(.focused) {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
- opacity: 0.9;
}
.pod.focused {
box-shadow: 0 16px 21px rgba(0, 0, 0, 0.2);
+ transform: scale(1) !important;
}
.pod.focused.locked {
.action-box-area {
background-color: #f5f5f5;
height: 24px;
+ /* Because of crbug.com/406529, the text in the .name div is janky if there's
+ an opacity transition in this div. */
+ transition: none;
width: 24px;
}
.action-box-area.active .action-box-button {
border-top: 6px solid #4c4c4c;
}
+
+.action-box-remove-user-warning .remove-warning-button {
+ height: 30px;
+}
+
+.action-box-remove-user-warning .remove-warning-button:focus {
+ /* Override the default blue border inherited from
+ button.custom-appearance:focus. */
+ border: 1px solid transparent !important;
+ box-shadow: inset 0 0 0 1px #fff;
+}
// Start a new report if this profile participates in safe browsing and there
// are process-wide incidents.
- if (safe_browsing_enabled && GetProfileContext(NULL))
+ if (safe_browsing_enabled && GetProfileContext(NULL) &&
+ GetProfileContext(NULL)->incidents.size()) {
BeginReportProcessing();
+ }
// TODO(grt): register for pref change notifications to start delayed analysis
// and/or report processing if sb is currently disabled but subsequently
ExpectTestIncidentUploaded(1);
}
+TEST_F(IncidentReportingServiceTest, NoCollectionWithoutIncident) {
+ // Register a callback.
+ RegisterAnalysis(ON_DELAYED_ANALYSIS_NO_ACTION);
+
+ // Let all tasks run.
+ task_runner_->RunUntilIdle();
+
+ // Confirm that the callback was not run.
+ ASSERT_FALSE(DelayedAnalysisRan());
+
+ // No collection should have taken place.
+ ASSERT_FALSE(HasCollectedEnvironmentData());
+
+ // Add a profile that participates in safe browsing.
+ CreateProfile(
+ "profile1", SAFE_BROWSING_OPT_IN, ON_PROFILE_ADDITION_NO_ACTION);
+
+ // Let all tasks run.
+ task_runner_->RunUntilIdle();
+
+ // Confirm that the callback was run.
+ ASSERT_TRUE(DelayedAnalysisRan());
+
+ // Still no collection should have taken place.
+ ASSERT_FALSE(HasCollectedEnvironmentData());
+}
+
// Tests that delayed analysis callbacks are called following the addition of a
// profile that participates in safe browsing.
TEST_F(IncidentReportingServiceTest, AnalysisAfterProfile) {
if (!AddrIsInCodeSection(address, mem_code_addr, code_size))
return true;
- UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.ModuleBaseRelocation", type);
-
switch (type) {
- case IMAGE_REL_BASED_HIGHLOW: {
- AddBytesCorrectedByReloc(reinterpret_cast<uintptr_t>(address), state);
- break;
- }
- case IMAGE_REL_BASED_ABSOLUTE:
+ case IMAGE_REL_BASED_ABSOLUTE: // 0
// Absolute type relocations are a noop, sometimes used to pad a section
// of relocations.
break;
- default: {
+ case IMAGE_REL_BASED_HIGHLOW: // 3
+ // The base relocation applies all 32 bits of the difference to the 32-bit
+ // field at offset.
+ AddBytesCorrectedByReloc(reinterpret_cast<uintptr_t>(address), state);
+ break;
+ case IMAGE_REL_BASED_DIR64: // 10
+ // The base relocation applies the difference to the 64-bit field at
+ // offset.
+ // TODO(robertshield): Handle this type of reloc.
+ break;
+ default:
// TODO(robertshield): Find a reliable description of the behaviour of the
// remaining types of relocation and handle them.
+ UMA_HISTOGRAM_SPARSE_SLOWLY("SafeBrowsing.ModuleBaseRelocation", type);
state->unknown_reloc_type = true;
break;
- }
}
return true;
}
{ "images/close_2_hover.png", IDR_CLOSE_2_H, "image/png" },
{ "images/close_2_active.png", IDR_CLOSE_2_P, "image/png" },
{ "images/close_2_white.png", IDR_CLOSE_2_MASK, "image/png" },
+ { "images/close_3_mask.png", IDR_CLOSE_3_MASK, "image/png" },
{ "images/google_logo.png", IDR_LOCAL_NTP_IMAGES_LOGO_PNG, "image/png" },
{ "images/white_google_logo.png",
IDR_LOCAL_NTP_IMAGES_WHITE_LOGO_PNG, "image/png" },
+ { "images/ntp_default_favicon.png", IDR_NTP_DEFAULT_FAVICON, "image/png" },
};
// Strips any query parameters from the specified path.
#include "chrome/browser/search/suggestions/suggestions_service_factory.h"
#include "chrome/common/url_constants.h"
#include "components/suggestions/suggestions_service.h"
+#include "components/suggestions/suggestions_utils.h"
#include "net/base/escape.h"
#include "ui/base/l10n/time_format.h"
#include "ui/gfx/codec/png_codec.h"
return;
}
+ // Since it's a debugging page, it's fine to specify that sync state is
+ // initialized.
suggestions_service->FetchSuggestionsData(
+ INITIALIZED_ENABLED_HISTORY,
base::Bind(&SuggestionsSource::OnSuggestionsAvailable,
weak_ptr_factory_.GetWeakPtr(), callback));
}
case EasyUnlockScreenlockStateHandler::STATE_PHONE_LOCKED:
case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY:
case EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE:
+ case EasyUnlockScreenlockStateHandler::STATE_PHONE_UNSUPPORTED:
return "chrome://theme/IDR_EASY_UNLOCK_LOCKED";
case EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING:
return "chrome://theme/IDR_EASY_UNLOCK_SPINNER";
return state == EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH ||
state == EasyUnlockScreenlockStateHandler::STATE_NO_PHONE ||
state == EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY ||
- state == EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE;
+ state == EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE ||
+ state == EasyUnlockScreenlockStateHandler::STATE_PHONE_UNSUPPORTED;
}
bool HasAnimation(EasyUnlockScreenlockStateHandler::State state) {
return state == EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING;
}
+bool HardlockOnClick(EasyUnlockScreenlockStateHandler::State state) {
+ return state != EasyUnlockScreenlockStateHandler::STATE_INACTIVE;
+}
+
size_t GetTooltipResourceId(EasyUnlockScreenlockStateHandler::State state) {
switch (state) {
case EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH:
case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY:
return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_NOT_NEARBY;
case EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED:
- // TODO(tbarzic): When hard lock is enabled change this to
- // IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_INSTRUCTIONS.
- return 0;
+ return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_INSTRUCTIONS;
+ case EasyUnlockScreenlockStateHandler::STATE_PHONE_UNSUPPORTED:
+ return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_UNSUPPORTED_ANDROID_VERSION;
default:
return 0;
}
}
+bool TooltipContainsDeviceType(EasyUnlockScreenlockStateHandler::State state) {
+ return state == EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED ||
+ state == EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE ||
+ state == EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH ||
+ state == EasyUnlockScreenlockStateHandler::STATE_PHONE_UNSUPPORTED;
+}
+
} // namespace
state_ = new_state;
- // If lock screen is not active, just cache the current state.
- // The screenlock state will get refreshed in |ScreenDidLock|.
- if (!screenlock_bridge_->IsLocked())
+ // If lock screen is not active or it forces offline password, just cache the
+ // current state. The screenlock state will get refreshed in |ScreenDidLock|.
+ if (!screenlock_bridge_->IsLocked() ||
+ screenlock_bridge_->lock_handler()->GetAuthType(user_email_) ==
+ ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD) {
return;
+ }
UpdateScreenlockAuthType();
}
icon_options.SetIconAsResourceURL(icon_url);
- UpdateTooltipOptions(&icon_options);
+ bool trial_run = IsTrialRun();
+
+ UpdateTooltipOptions(trial_run, &icon_options);
if (UseOpaqueIcon(state_))
icon_options.SetOpacity(kOpaqueIconOpacity);
if (HasAnimation(state_))
icon_options.SetAnimation(kSpinnerResourceWidth, kSpinnerIntervalMs);
+ // Hardlocking is disabled in trial run.
+ if (!trial_run && HardlockOnClick(state_))
+ icon_options.SetHardlockOnClick();
+
+ if (trial_run && state_ == STATE_AUTHENTICATED)
+ MarkTrialRunComplete();
+
screenlock_bridge_->lock_handler()->ShowUserPodCustomIcon(user_email_,
icon_options);
}
}
void EasyUnlockScreenlockStateHandler::UpdateTooltipOptions(
+ bool trial_run,
ScreenlockBridge::UserPodCustomIconOptions* icon_options) {
- bool show_tutorial = ShouldShowTutorial();
-
size_t resource_id = 0;
base::string16 device_name;
- if (show_tutorial) {
+ if (trial_run && state_ == STATE_AUTHENTICATED) {
resource_id = IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_TUTORIAL;
} else {
resource_id = GetTooltipResourceId(state_);
- if (state_ == STATE_AUTHENTICATED || state_ == STATE_PHONE_UNLOCKABLE)
+ if (TooltipContainsDeviceType(state_))
device_name = GetDeviceName();
}
if (tooltip.empty())
return;
- if (show_tutorial)
- MarkTutorialShown();
-
- icon_options->SetTooltip(tooltip, show_tutorial /* autoshow tooltip */);
+ icon_options->SetTooltip(
+ tooltip,
+ state_ == STATE_AUTHENTICATED && trial_run /* autoshow tooltip */);
}
-bool EasyUnlockScreenlockStateHandler::ShouldShowTutorial() {
- if (state_ != STATE_AUTHENTICATED)
- return false;
+bool EasyUnlockScreenlockStateHandler::IsTrialRun() {
return pref_service_ &&
pref_service_->GetBoolean(prefs::kEasyUnlockShowTutorial);
}
-void EasyUnlockScreenlockStateHandler::MarkTutorialShown() {
+void EasyUnlockScreenlockStateHandler::MarkTrialRunComplete() {
if (!pref_service_)
return;
pref_service_->SetBoolean(prefs::kEasyUnlockShowTutorial, false);
}
void EasyUnlockScreenlockStateHandler::UpdateScreenlockAuthType() {
+ if (screenlock_bridge_->lock_handler()->GetAuthType(user_email_) ==
+ ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD)
+ return;
+
if (state_ == STATE_AUTHENTICATED) {
screenlock_bridge_->lock_handler()->SetAuthType(
user_email_,
// A phone eligible to unlock the device is found, but it's not close enough
// to be allowed to unlock the device.
STATE_PHONE_NOT_NEARBY,
+ // An Easy Unlock enabled phone is found, but it is not allowed to unlock
+ // the device because it does not support reporting it's lock screen state.
+ STATE_PHONE_UNSUPPORTED,
// The device can be unlocked using Easy Unlock.
STATE_AUTHENTICATED
};
virtual void OnScreenDidLock() OVERRIDE;
virtual void OnScreenDidUnlock() OVERRIDE;
+ // Updates icon's tooltip options.
+ // |trial_run|: Whether the trial Easy Unlock run is in progress.
void UpdateTooltipOptions(
+ bool trial_run,
ScreenlockBridge::UserPodCustomIconOptions* icon_options);
- // Whether the tutorial message should be shown to the user. The message is
- // shown only once, when the user encounters STATE_AUTHENTICATED for the first
- // time (across sessions). After the tutorial message is shown,
- // |MarkTutorialShown| should be called to prevent further tutorial message.
- bool ShouldShowTutorial();
+ // Whether this is the first, trial Easy Unlock run. If this is the case, a
+ // tutorial message should be shown and hard-locking be disabled in
+ // Authenticated state. The trial run will be active if Easy Unlock never
+ // entered Authenticated state (across sessions).
+ bool IsTrialRun();
- // Sets user preference that prevents showing of tutorial messages.
- void MarkTutorialShown();
+ // Sets user preference that marks trial run completed.
+ void MarkTrialRunComplete();
// Gets the name to be used for the device. The name depends on the device
// type (example values: Chromebook and Chromebox).
user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
registry->RegisterBooleanPref(
prefs::kEasyUnlockShowTutorial,
- false,
+ true,
user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
registry->RegisterDictionaryPref(
prefs::kEasyUnlockPairing,
base::Bind(&EasyUnlockService::OnPrefsChanged, base::Unretained(this)));
OnPrefsChanged();
+#if defined(OS_CHROMEOS)
+ // Only start Bluetooth detection for ChromeOS since the feature is
+ // only offered on ChromeOS. Enabling this on non-ChromeOS platforms
+ // previously introduced a performance regression: http://crbug.com/404482
+ // Make sure not to reintroduce a performance regression if re-enabling on
+ // additional platforms.
+ // TODO(xiyuan): Revisit when non-chromeos platforms are supported.
bluetooth_detector_->Initialize();
+#endif // defined(OS_CHROMEOS)
}
void EasyUnlockService::LoadApp() {
ClearRemoteDevices();
SetTurnOffFlowStatus(IDLE);
+ // Make sure lock screen state set by the extension gets reset.
+ screenlock_state_handler_.reset();
+
if (GetComponentLoader(profile_)->Exists(extension_misc::kEasyUnlockAppId)) {
extensions::ExtensionSystem* extension_system =
extensions::ExtensionSystem::Get(profile_);
animation_resource_width_(0u),
animation_frame_length_ms_(0u),
opacity_(100u),
- autoshow_tooltip_(false) {
+ autoshow_tooltip_(false),
+ hardlock_on_click_(false) {
}
ScreenlockBridge::UserPodCustomIconOptions::~UserPodCustomIconOptions() {}
animation_frame_length_ms_);
result->Set("animation", animation);
}
+
+ if (hardlock_on_click_)
+ result->SetBoolean("hardlockOnClick", true);
+
return result.Pass();
}
autoshow_tooltip_ = autoshow;
}
+void ScreenlockBridge::UserPodCustomIconOptions::SetHardlockOnClick() {
+ hardlock_on_click_ = true;
+}
+
// static
std::string ScreenlockBridge::GetAuthenticatedUserEmail(Profile* profile) {
// |profile| has to be a signed-in profile with SigninManager already
// shown with the icon.
void SetTooltip(const base::string16& tooltip, bool autoshow);
+ // If hardlock on click is set, clicking the icon in the screenlock will
+ // go to state where password is required for unlock.
+ void SetHardlockOnClick();
+
private:
std::string icon_resource_url_;
scoped_ptr<gfx::Image> icon_image_;
base::string16 tooltip_;
bool autoshow_tooltip_;
+ bool hardlock_on_click_;
+
DISALLOW_COPY_AND_ASSIGN(UserPodCustomIconOptions);
};
NUMERIC_PIN = 2,
USER_CLICK = 3,
EXPAND_THEN_USER_CLICK = 4,
+ FORCE_OFFLINE_PASSWORD = 5
};
// Displays |message| in a banner on the lock screen.
last_get_token_error_ = error;
switch (error.state()) {
case GoogleServiceAuthError::CONNECTION_FAILED:
+ case GoogleServiceAuthError::REQUEST_CANCELED:
+ case GoogleServiceAuthError::SERVICE_ERROR:
case GoogleServiceAuthError::SERVICE_UNAVAILABLE: {
// Transient error. Retry after some time.
request_access_token_backoff_.InformOfRequest(false);
NotifyObservers();
break;
}
- case GoogleServiceAuthError::SERVICE_ERROR:
case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: {
if (!sync_prefs_.SyncHasAuthError()) {
sync_prefs_.SetSyncAuthError(true);
// Fallthrough.
}
default: {
+ if (error.state() != GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) {
+ LOG(ERROR) << "Unexpected persistent error: " << error.ToString();
+ }
// Show error to user.
UpdateAuthErrorState(error);
}
GetSyncService((0))->GetAuthError().state());
}
-// Verify that ProfileSyncService ends up with an SERVICE_ERROR auth error when
+// Verify that ProfileSyncService retries after SERVICE_ERROR auth error when
// an invalid_client error is returned by OAuth2TokenService with an
// HTTP_BAD_REQUEST (400) response code.
-IN_PROC_BROWSER_TEST_F(SyncAuthTest, InvalidClient) {
+IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryInvalidClient) {
ASSERT_TRUE(SetupSync());
ASSERT_FALSE(AttemptToTriggerAuthError());
GetFakeServer()->SetUnauthenticated();
net::HTTP_BAD_REQUEST,
net::URLRequestStatus::SUCCESS);
ASSERT_TRUE(AttemptToTriggerAuthError());
- ASSERT_EQ(GoogleServiceAuthError::SERVICE_ERROR,
- GetSyncService((0))->GetAuthError().state());
+ ASSERT_TRUE(GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
}
-// Verify that ProfileSyncService ends up with a REQUEST_CANCELED auth error
-// when when OAuth2TokenService has encountered a URLRequestStatus of CANCELED.
-IN_PROC_BROWSER_TEST_F(SyncAuthTest, RequestCanceled) {
+// Verify that ProfileSyncService retries after REQUEST_CANCELED auth error
+// when OAuth2TokenService has encountered a URLRequestStatus of CANCELED.
+IN_PROC_BROWSER_TEST_F(SyncAuthTest, RetryRequestCanceled) {
ASSERT_TRUE(SetupSync());
ASSERT_FALSE(AttemptToTriggerAuthError());
GetFakeServer()->SetUnauthenticated();
net::HTTP_INTERNAL_SERVER_ERROR,
net::URLRequestStatus::CANCELED);
ASSERT_TRUE(AttemptToTriggerAuthError());
- ASSERT_EQ(GoogleServiceAuthError::REQUEST_CANCELED,
- GetSyncService((0))->GetAuthError().state());
+ ASSERT_TRUE(GetSyncService((0))->IsRetryingAccessTokenFetchForTest());
}
// Verify that ProfileSyncService fails initial sync setup during backend
int render_process_id,
int render_frame_id,
const content::URLDataSource::GotDataCallback& callback) {
+ if (!profile_->GetTopSites()) {
+ callback.Run(NULL);
+ return;
+ }
+
profile_->GetTopSites()->GetMostVisitedURLs(
base::Bind(&ThumbnailListSource::OnMostVisitedURLsAvailable,
weak_ptr_factory_.GetWeakPtr(),
ScopedJavaLocalRef<jstring> description = ConvertUTF8ToJavaString(
env, identity_info.identity_status_description);
- Java_WebsiteSettingsPopup_addDescriptionSection(
- env,
- popup_jobject_.obj(),
- icon_id,
- ConvertUTF8ToJavaString(env, headline).obj(),
- description.obj());
-
base::string16 certificate_label =
l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON);
Java_WebsiteSettingsPopup_addCertificateSection(
#include "base/memory/scoped_ptr.h"
#include "base/strings/stringprintf.h"
+#include "chrome/browser/app_mode/app_mode_utils.h"
#include "chrome/browser/favicon/favicon_tab_helper.h"
#include "chrome/browser/file_select_helper.h"
#include "chrome/browser/media/media_capture_devices_dispatcher.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
+#include "chrome/browser/ui/zoom/zoom_controller.h"
#include "chrome/common/extensions/chrome_extension_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_view_host.h"
printing::PrintViewManagerBasic::CreateForWebContents(web_contents);
#endif // defined(ENABLE_FULL_PRINTING)
#endif // defined(ENABLE_PRINTING)
+
+ // Kiosk app supports zooming.
+ if (chrome::IsRunningInForcedAppMode())
+ ZoomController::CreateForWebContents(web_contents);
}
content::WebContents* ChromeAppDelegate::OpenURLFromTab(
// Spacing between the border of the popup and any text.
static const int kHorizontalPadding = 10;
+ // Desired height of the password section.
+ static const int kPopupPasswordSectionHeight = 62;
+
// Called by the view when the saved passwords link is clicked.
virtual void OnSavedPasswordsLinkClicked() = 0;
#include "base/logging.h"
#include "chrome/browser/ui/autofill/autofill_popup_controller.h"
#import "chrome/browser/ui/cocoa/autofill/password_generation_popup_view_cocoa.h"
-#include "ui/base/cocoa/window_size_constants.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
namespace autofill {
}
gfx::Size PasswordGenerationPopupViewBridge::GetPreferredSizeOfPasswordView() {
- // TODO(gcasto): Implement this function.
- return gfx::Size();
+ return gfx::Size(NSSizeToCGSize([view_ preferredSize]));
}
void PasswordGenerationPopupViewBridge::UpdateBoundsAndRedrawPopup() {
bool PasswordGenerationPopupViewBridge::IsPointInPasswordBounds(
const gfx::Point& point) {
- // TODO(gcasto): Implement this function.
- return true;
+ return [view_ isPointInPasswordBounds:NSPointFromCGPoint(point.ToCGPoint())];
}
PasswordGenerationPopupView* PasswordGenerationPopupView::Create(
#import <Cocoa/Cocoa.h>
+#include "base/mac/scoped_nsobject.h"
#include "chrome/browser/ui/autofill/password_generation_popup_controller.h"
#import "chrome/browser/ui/cocoa/autofill/autofill_popup_base_view_cocoa.h"
// The cross-platform controller for this view.
__weak autofill::PasswordGenerationPopupController* controller_;
- __weak NSTextField* passwordField_;
- __weak NSTextField* passwordSubtextField_;
- __weak HyperlinkTextView* helpTextView_;
+ base::scoped_nsobject<NSView> passwordSection_;
+ base::scoped_nsobject<NSTextField> passwordField_;
+ base::scoped_nsobject<NSTextField> passwordTitleField_;
+ base::scoped_nsobject<NSImageView> keyIcon_;
+ base::scoped_nsobject<NSBox> divider_;
+ base::scoped_nsobject<HyperlinkTextView> helpTextView_;
}
// Designated initializer.
(autofill::PasswordGenerationPopupController*)controller
frame:(NSRect)frame;
+// Determines whether |point| falls inside the password section of the popup.
+// |point| needs to be in the popup's coordinate system.
+- (BOOL)isPointInPasswordBounds:(NSPoint)point;
+
// Informs the view that its controller has been (or will imminently be)
// destroyed.
- (void)controllerDestroyed;
+// The preferred size for the popup.
+- (NSSize)preferredSize;
+
@end
#endif // CHROME_BROWSER_UI_COCOA_AUTOFILL_PASSWORD_GENERATION_POPUP_VIEW_COCOA_H_
#import "chrome/browser/ui/cocoa/autofill/password_generation_popup_view_cocoa.h"
+#include <cmath>
+
#include "base/logging.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/ui/autofill/autofill_popup_controller.h"
#import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
#import "chrome/browser/ui/cocoa/l10n_util.h"
#include "components/autofill/core/browser/popup_item_ids.h"
+#include "grit/theme_resources.h"
#include "skia/ext/skia_utils_mac.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/text_constants.h"
using autofill::AutofillPopupView;
+using autofill::PasswordGenerationPopupController;
using autofill::PasswordGenerationPopupView;
using base::scoped_nsobject;
namespace {
+// The height of the divider between the password and help sections, in pixels.
+const CGFloat kDividerHeight = 1;
+
+// The amount of whitespace, in pixels, between lines of text in the password
+// section.
+const CGFloat kPasswordSectionVerticalSeparation = 5;
+
NSColor* DividerColor() {
return gfx::SkColorToCalibratedNSColor(
PasswordGenerationPopupView::kDividerColor);
if (self = [super initWithDelegate:controller frame:frame]) {
controller_ = controller;
- passwordField_ = [self textFieldWithText:controller_->password()
- color:[self nameColor]
- alignment:NSLeftTextAlignment];
- [self addSubview:passwordField_];
-
- passwordSubtextField_ = [self textFieldWithText:controller_->SuggestedText()
- color:[self subtextColor]
- alignment:NSRightTextAlignment];
- [self addSubview:passwordSubtextField_];
-
- scoped_nsobject<HyperlinkTextView> helpTextView(
- [[HyperlinkTextView alloc] initWithFrame:NSZeroRect]);
- [helpTextView setMessage:base::SysUTF16ToNSString(controller_->HelpText())
- withFont:[self textFont]
- messageColor:HelpTextColor()];
- [helpTextView addLinkRange:controller_->HelpTextLinkRange().ToNSRange()
- withName:@""
- linkColor:HelpLinkColor()];
- [helpTextView setDelegate:self];
- [[helpTextView textContainer] setLineFragmentPadding:0.0f];
- [helpTextView setVerticallyResizable:YES];
- [self addSubview:helpTextView];
- helpTextView_ = helpTextView.get();
- }
+ passwordSection_.reset([[NSView alloc] initWithFrame:NSZeroRect]);
+ [self addSubview:passwordSection_];
+
+ passwordField_.reset(
+ [[self textFieldWithText:controller_->password()
+ attributes:[self passwordAttributes]] retain]);
+ [passwordSection_ addSubview:passwordField_];
+
+ passwordTitleField_.reset(
+ [[self textFieldWithText:controller_->SuggestedText()
+ attributes:[self passwordTitleAttributes]] retain]);
+ [passwordSection_ addSubview:passwordTitleField_];
+
+ keyIcon_.reset([[NSImageView alloc] initWithFrame:NSZeroRect]);
+ NSImage* keyImage = ResourceBundle::GetSharedInstance()
+ .GetImageNamed(IDR_GENERATE_PASSWORD_KEY)
+ .ToNSImage();
+ [keyIcon_ setImage:keyImage];
+ [passwordSection_ addSubview:keyIcon_];
+
+ divider_.reset([[NSBox alloc] initWithFrame:NSZeroRect]);
+ [divider_ setBoxType:NSBoxCustom];
+ [divider_ setBorderType:NSLineBorder];
+ [divider_ setBorderColor:DividerColor()];
+ [self addSubview:divider_];
+
+ helpTextView_.reset([[HyperlinkTextView alloc] initWithFrame:NSZeroRect]);
+ [helpTextView_ setMessage:base::SysUTF16ToNSString(controller_->HelpText())
+ withFont:[self textFont]
+ messageColor:HelpTextColor()];
+ [helpTextView_ addLinkRange:controller_->HelpTextLinkRange().ToNSRange()
+ withName:@""
+ linkColor:HelpLinkColor()];
+ [helpTextView_ setDelegate:self];
+ [helpTextView_ setDrawsBackground:YES];
+ [helpTextView_ setBackgroundColor:HelpTextBackgroundColor()];
+ [helpTextView_
+ setTextContainerInset:NSMakeSize(controller_->kHorizontalPadding,
+ controller_->kHelpVerticalPadding)];
+ // Remove the underlining.
+ NSTextStorage* text = [helpTextView_ textStorage];
+ [text addAttribute:NSUnderlineStyleAttributeName
+ value:@(NSUnderlineStyleNone)
+ range:controller_->HelpTextLinkRange().ToNSRange()];
+ [self addSubview:helpTextView_];
+}
return self;
}
#pragma mark NSView implementation:
- (void)drawRect:(NSRect)dirtyRect {
+ [super drawRect:dirtyRect];
+
// If the view is in the process of being destroyed, don't bother drawing.
if (!controller_)
return;
if (controller_->password_selected()) {
// Draw a highlight under the suggested password.
- NSRect highlightBounds = [self passwordBounds];
+ NSRect highlightBounds = [passwordSection_ frame];
[[self highlightColor] set];
[NSBezierPath fillRect:highlightBounds];
}
-
- // Render the background of the help text.
- [HelpTextBackgroundColor() set];
- [NSBezierPath fillRect:[self helpBounds]];
-
- // Render the divider.
- [DividerColor() set];
- [NSBezierPath fillRect:[self dividerBounds]];
}
#pragma mark Public API:
+- (NSSize)preferredSize {
+ const NSSize passwordTitleSize =
+ [base::SysUTF16ToNSString(controller_->SuggestedText())
+ sizeWithAttributes:@{ NSFontAttributeName : [self boldFont] }];
+ const NSSize passwordSize = [base::SysUTF16ToNSString(controller_->password())
+ sizeWithAttributes:@{ NSFontAttributeName : [self textFont] }];
+
+ CGFloat width =
+ autofill::kPopupBorderThickness +
+ controller_->kHorizontalPadding +
+ [[keyIcon_ image] size].width +
+ controller_->kHorizontalPadding +
+ std::max(passwordSize.width, passwordTitleSize.width) +
+ controller_->kHorizontalPadding +
+ autofill::kPopupBorderThickness;
+
+ width = std::max(width, (CGFloat)controller_->GetMinimumWidth());
+
+ CGFloat height =
+ autofill::kPopupBorderThickness +
+ controller_->kHelpVerticalPadding +
+ [self helpSizeForPopupWidth:width].height +
+ controller_->kHelpVerticalPadding +
+ autofill::kPopupBorderThickness;
+
+ if (controller_->display_password())
+ height += controller_->kPopupPasswordSectionHeight;
+
+ return NSMakeSize(width, height);
+}
+
- (void)updateBoundsAndRedrawPopup {
- [self positionView:passwordField_ inRect:[self passwordBounds]];
- [self positionView:passwordSubtextField_ inRect:[self passwordBounds]];
- [self positionView:helpTextView_ inRect:[self helpBounds]];
+ const CGFloat popupWidth = controller_->popup_bounds().width();
+ const CGFloat contentWidth =
+ popupWidth - (2 * autofill::kPopupBorderThickness);
+ const CGFloat contentHeight = controller_->popup_bounds().height() -
+ (2 * autofill::kPopupBorderThickness);
+
+ if (controller_->display_password()) {
+ // The password can change while the bubble is shown: If the user has
+ // accepted the password and then selects the form again and starts deleting
+ // the password, the field will be initially invisible and then become
+ // visible.
+ [self updatePassword];
+
+ // Lay out the password section, which includes the key icon, the title, and
+ // the suggested password.
+ [passwordSection_
+ setFrame:NSMakeRect(autofill::kPopupBorderThickness,
+ autofill::kPopupBorderThickness,
+ contentWidth,
+ controller_->kPopupPasswordSectionHeight)];
+
+ // The key icon falls to the left of the title and password.
+ const NSSize imageSize = [[keyIcon_ image] size];
+ const CGFloat keyX = controller_->kHorizontalPadding;
+ const CGFloat keyY =
+ std::ceil((controller_->kPopupPasswordSectionHeight / 2.0) -
+ (imageSize.height / 2.0));
+ [keyIcon_ setFrame:{ NSMakePoint(keyX, keyY), imageSize }];
+
+ // The title and password fall to the right of the key icon and are centered
+ // vertically as a group with some padding in between.
+ [passwordTitleField_ sizeToFit];
+ [passwordField_ sizeToFit];
+ const CGFloat groupHeight = NSHeight([passwordField_ frame]) +
+ kPasswordSectionVerticalSeparation +
+ NSHeight([passwordTitleField_ frame]);
+ const CGFloat groupX =
+ NSMaxX([keyIcon_ frame]) + controller_->kHorizontalPadding;
+ const CGFloat groupY =
+ std::ceil((controller_->kPopupPasswordSectionHeight / 2.0) -
+ (groupHeight / 2.0));
+ [passwordField_ setFrameOrigin:NSMakePoint(groupX, groupY)];
+ const CGFloat titleY = groupY +
+ NSHeight([passwordField_ frame]) +
+ kPasswordSectionVerticalSeparation;
+ [passwordTitleField_ setFrameOrigin:NSMakePoint(groupX, titleY)];
+
+ // Layout the divider, which falls immediately below the password section.
+ const CGFloat dividerX = autofill::kPopupBorderThickness;
+ const CGFloat dividerY = NSMaxY([passwordSection_ frame]);
+ NSRect dividerFrame =
+ NSMakeRect(dividerX, dividerY, contentWidth, kDividerHeight);
+ [divider_ setFrame:dividerFrame];
+ }
+
+ // Layout the help section beneath the divider (if applicable, otherwise
+ // beneath the border).
+ const CGFloat helpX = autofill::kPopupBorderThickness;
+ const CGFloat helpY = controller_->display_password()
+ ? NSMaxY([divider_ frame])
+ : autofill::kPopupBorderThickness;
+ const CGFloat helpHeight = contentHeight -
+ NSHeight([passwordSection_ frame]) -
+ NSHeight([divider_ frame]);
+ [helpTextView_ setFrame:NSMakeRect(helpX, helpY, contentWidth, helpHeight)];
[super updateBoundsAndRedrawPopup];
}
+- (BOOL)isPointInPasswordBounds:(NSPoint)point {
+ return NSPointInRect(point, [passwordSection_ frame]);
+}
+
- (void)controllerDestroyed {
controller_ = NULL;
[super delegateDestroyed];
#pragma mark Private helpers:
-- (NSTextField*)textFieldWithText:(const base::string16&)text
- color:(NSColor*)color
- alignment:(NSTextAlignment)alignment {
+- (void)updatePassword {
+ base::scoped_nsobject<NSMutableAttributedString> updatedPassword(
+ [[NSMutableAttributedString alloc]
+ initWithString:base::SysUTF16ToNSString(controller_->password())
+ attributes:[self passwordAttributes]]);
+ [passwordField_ setAttributedStringValue:updatedPassword];
+}
+
+- (NSDictionary*)passwordTitleAttributes {
scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
[[NSMutableParagraphStyle alloc] init]);
- [paragraphStyle setAlignment:alignment];
+ [paragraphStyle setAlignment:NSLeftTextAlignment];
+ return @{
+ NSFontAttributeName : [self boldFont],
+ NSForegroundColorAttributeName : [self nameColor],
+ NSParagraphStyleAttributeName : paragraphStyle.autorelease()
+ };
+}
- NSDictionary* textAttributes = @{
+- (NSDictionary*)passwordAttributes {
+ scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
+ [[NSMutableParagraphStyle alloc] init]);
+ [paragraphStyle setAlignment:NSLeftTextAlignment];
+ return @{
NSFontAttributeName : [self textFont],
- NSForegroundColorAttributeName : color,
- NSParagraphStyleAttributeName : paragraphStyle
+ NSForegroundColorAttributeName : [self nameColor],
+ NSParagraphStyleAttributeName : paragraphStyle.autorelease()
};
+}
+- (NSTextField*)textFieldWithText:(const base::string16&)text
+ attributes:(NSDictionary*)attributes {
+ NSTextField* textField =
+ [[[NSTextField alloc] initWithFrame:NSZeroRect] autorelease];
scoped_nsobject<NSAttributedString> attributedString(
[[NSAttributedString alloc]
initWithString:base::SysUTF16ToNSString(text)
- attributes:textAttributes]);
-
- NSTextField* textField =
- [[[NSTextField alloc] initWithFrame:NSZeroRect] autorelease];
- [textField setAttributedStringValue:attributedString];
+ attributes:attributes]);
+ [textField setAttributedStringValue:attributedString.autorelease()];
[textField setEditable:NO];
[textField setSelectable:NO];
[textField setDrawsBackground:NO];
return textField;
}
-- (void)positionView:(NSView*)view inRect:(NSRect)bounds {
- NSRect frame = NSInsetRect(bounds, controller_->kHorizontalPadding, 0);
- [view setFrame:frame];
-
- // Center the text vertically within the bounds.
- NSSize delta = cocoa_l10n_util::WrapOrSizeToFit(view);
- [view setFrameOrigin:
- NSInsetRect(frame, 0, floor(-delta.height/2)).origin];
-}
-
-- (NSRect)passwordBounds {
- return NSZeroRect;
-}
-
-- (NSRect)helpBounds {
- return NSZeroRect;
+- (NSSize)helpSizeForPopupWidth:(CGFloat)width {
+ const CGFloat helpWidth = width -
+ 2 * controller_->kHorizontalPadding -
+ 2 * autofill::kPopupBorderThickness;
+ const NSSize size = NSMakeSize(helpWidth, MAXFLOAT);
+ NSRect textFrame = [base::SysUTF16ToNSString(controller_->HelpText())
+ boundingRectWithSize:size
+ options:NSLineBreakByWordWrapping |
+ NSStringDrawingUsesLineFragmentOrigin
+ attributes:@{ NSFontAttributeName : [self textFont] }];
+ return textFrame.size;
}
-- (NSRect)dividerBounds {
- return NSZeroRect;
+- (NSFont*)boldFont {
+ return [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]];
}
- (NSFont*)textFont {
- return ResourceBundle::GetSharedInstance().GetFontList(
- ResourceBundle::SmallFont).GetPrimaryFont().GetNativeFont();
+ return [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
}
@end
// fullscreen mode.
BOOL enteringFullscreen_;
+ // True when entering system fullscreen.
+ BOOL enteringSystemFullscreen_;
+
// True between |-setPresentationMode:url:bubbleType:| and
// |-windowDidEnterFullScreen:| to indicate that the window is in the process
// of transitioning into fullscreen presentation mode.
maxY -= tabStripHeight;
[tabStripView setFrame:NSMakeRect(0, maxY, width, tabStripHeight)];
+ // In Yosemite fullscreen, manually add the fullscreen controls to the tab
+ // strip.
+ BOOL isInAppKitFullscreen =
+ [self isInSystemFullscreen] || enteringSystemFullscreen_;
+ BOOL addControlsInFullscreen =
+ isInAppKitFullscreen && base::mac::IsOSYosemiteOrLater();
+
// Set left indentation based on fullscreen mode status.
- [tabStripController_ setLeftIndentForControls:(fullscreen ? 0 :
- [[tabStripController_ class] defaultLeftIndentForControls])];
+ CGFloat leftIndent = 0;
+ if (!fullscreen || addControlsInFullscreen)
+ leftIndent = [[tabStripController_ class] defaultLeftIndentForControls];
+ [tabStripController_ setLeftIndentForControls:leftIndent];
+
+ if (addControlsInFullscreen)
+ [tabStripController_ addWindowControls];
+ else
+ [tabStripController_ removeWindowControls];
// Lay out the icognito/avatar badge because calculating the indentation on
// the right depends on it.
BOOL mode = enteringPresentationMode_ ||
browser_->fullscreen_controller()->IsWindowFullscreenForTabOrPending();
enteringFullscreen_ = YES;
+ enteringSystemFullscreen_ = YES;
[self setPresentationModeInternal:mode forceDropdown:NO];
}
if (notification) // For System Fullscreen when non-nil.
[self deregisterForContentViewResizeNotifications];
enteringFullscreen_ = NO;
+ enteringSystemFullscreen_ = NO;
enteringPresentationMode_ = NO;
const CommandLine* command_line = CommandLine::ForCurrentProcess();
- (void)windowDidFailToEnterFullScreen:(NSWindow*)window {
[self deregisterForContentViewResizeNotifications];
enteringFullscreen_ = NO;
+ enteringSystemFullscreen_ = NO;
[self setPresentationModeInternal:NO forceDropdown:NO];
// Force a relayout to try and get the window back into a reasonable state.
ASSERT_TRUE(profile_manager_.SetUp());
- profile_ = profile_manager_.CreateTestingProfile("default");
+ profile_ = profile_manager_.CreateTestingProfile("Person 1");
ASSERT_TRUE(profile_);
// TODO(shess): These are needed in case someone creates a browser
public:
ConstrainedWebDialogDelegateMac(
content::BrowserContext* browser_context,
- WebDialogDelegate* delegate,
- WebDialogWebContentsDelegate* tab_delegate)
- : ConstrainedWebDialogDelegateBase(
- browser_context, delegate, tab_delegate) {}
+ WebDialogDelegate* delegate)
+ : ConstrainedWebDialogDelegateBase(browser_context, delegate, NULL) {}
// WebDialogWebContentsDelegate interface.
virtual void CloseContents(WebContents* source) OVERRIDE {
ConstrainedWebDialogDelegateViewMac(
content::BrowserContext* browser_context,
WebDialogDelegate* delegate,
- WebDialogWebContentsDelegate* tab_delegate,
content::WebContents* web_contents);
virtual ~ConstrainedWebDialogDelegateViewMac() {}
ConstrainedWebDialogDelegateViewMac::ConstrainedWebDialogDelegateViewMac(
content::BrowserContext* browser_context,
WebDialogDelegate* delegate,
- WebDialogWebContentsDelegate* tab_delegate,
content::WebContents* web_contents)
- : impl_(new ConstrainedWebDialogDelegateMac(browser_context,
- delegate,
- tab_delegate)) {
+ : impl_(new ConstrainedWebDialogDelegateMac(browser_context, delegate)) {
// Create a window to hold web_contents in the constrained sheet:
gfx::Size size;
delegate->GetDialogSize(&size);
ConstrainedWebDialogDelegate* CreateConstrainedWebDialog(
content::BrowserContext* browser_context,
WebDialogDelegate* delegate,
- WebDialogWebContentsDelegate* tab_delegate,
content::WebContents* web_contents) {
// Deleted when the dialog closes.
ConstrainedWebDialogDelegateViewMac* constrained_delegate =
new ConstrainedWebDialogDelegateViewMac(
- browser_context, delegate, tab_delegate, web_contents);
+ browser_context, delegate, web_contents);
return constrained_delegate;
}
const int kBezelThickness = 3; // Width of the bezel on an NSButton.
const int kImageTitleSpacing = 10;
const int kBlueButtonHeight = 30;
+const CGFloat kFocusRingLineWidth = 2;
// Fixed size for embedded sign in pages as defined in Gaia.
const CGFloat kFixedGaiaViewWidth = 360;
const CGFloat kFixedAccountRemovalViewWidth = 280;
// Fixed size for the switch user view.
-const int kFixedSwitchUserViewWidth = 280;
+const int kFixedSwitchUserViewWidth = 320;
// The tag number for the primary account.
const int kPrimaryProfileTag = -1;
[container addSubview:button];
[container addSubview:title_label];
CGFloat height = std::max(NSMaxY([title_label frame]),
- NSMaxY([button frame])) + kSmallVerticalSpacing;
+ NSMaxY([button frame])) + kVerticalSpacing;
[container setFrameSize:NSMakeSize(NSWidth([container frame]), height)];
return container.autorelease();
return buttonSize;
}
-@end
+- (NSFocusRingType)focusRingType {
+ // This is taken care of by the custom drawing code.
+ return NSFocusRingTypeNone;
+}
-// A custom button that has a transparent backround.
-@interface TransparentBackgroundButton : NSButton
-@end
+- (void)drawWithFrame:(NSRect)frame inView:(NSView *)controlView {
+ [super drawInteriorWithFrame:frame inView:controlView];
-@implementation TransparentBackgroundButton
-- (id)initWithFrame:(NSRect)frameRect {
- if ((self = [super initWithFrame:frameRect])) {
- [self setBordered:NO];
- [self setFont:[NSFont labelFontOfSize:kTextFontSize]];
- [self setButtonType:NSMomentaryChangeButton];
+ // Focus ring.
+ if ([self showsFirstResponder]) {
+ NSRect focusRingRect =
+ NSInsetRect(frame, kFocusRingLineWidth, kFocusRingLineWidth);
+ // TODO(noms): When we are targetting 10.7, we should change this to use
+ // -drawFocusRingMaskWithFrame instead.
+ [[[NSColor keyboardFocusIndicatorColor] colorWithAlphaComponent:1] set];
+ NSBezierPath* path = [NSBezierPath bezierPathWithRect:focusRingRect];
+ [path setLineWidth:kFocusRingLineWidth];
+ [path stroke];
}
- return self;
}
+@end
+
+// A custom image view that has a transparent backround.
+@interface TransparentBackgroundImageView : NSImageView
+@end
+
+@implementation TransparentBackgroundImageView
- (void)drawRect:(NSRect)dirtyRect {
NSColor* backgroundColor = [NSColor colorWithCalibratedWhite:1 alpha:0.6f];
[backgroundColor setFill];
}
@end
+@interface CustomCircleImageCell : NSButtonCell
+@end
+
+@implementation CustomCircleImageCell
+- (void)drawWithFrame:(NSRect)frame inView:(NSView *)controlView {
+ // Display everything as a circle that spans the entire control.
+ NSBezierPath* path = [NSBezierPath bezierPathWithOvalInRect:frame];
+ [path addClip];
+
+ [super drawImage:[self image] withFrame:frame inView:controlView];
+
+ // Focus ring.
+ if ([self showsFirstResponder]) {
+ [[[NSColor keyboardFocusIndicatorColor] colorWithAlphaComponent:1] set];
+ [path setLineWidth:kFocusRingLineWidth];
+ [path stroke];
+ }
+}
+@end
+
// A custom image control that shows a "Change" button when moused over.
-@interface EditableProfilePhoto : NSImageView {
+@interface EditableProfilePhoto : HoverImageButton {
@private
AvatarMenu* avatarMenu_; // Weak; Owned by ProfileChooserController.
- base::scoped_nsobject<TransparentBackgroundButton> changePhotoButton_;
- // Used to display the "Change" button on hover.
- ui::ScopedCrTrackingArea trackingArea_;
+ base::scoped_nsobject<TransparentBackgroundImageView> changePhotoImage_;
ProfileChooserController* controller_;
}
// Called when the "Change" button is clicked.
- (void)editPhoto:(id)sender;
-// When hovering over the profile photo, show the "Change" button.
-- (void)mouseEntered:(NSEvent*)event;
-
-// When hovering away from the profile photo, hide the "Change" button.
-- (void)mouseExited:(NSEvent*)event;
-@end
-
-@interface EditableProfilePhoto (Private)
-// Create the "Change" avatar photo button.
-- (TransparentBackgroundButton*)changePhotoButtonWithRect:(NSRect)rect;
@end
@implementation EditableProfilePhoto
if ((self = [super initWithFrame:frameRect])) {
avatarMenu_ = avatarMenu;
controller_ = controller;
- [self setImage:CreateProfileImage(
- profileIcon, kLargeImageSide).ToNSImage()];
- // Add a tracking area so that we can show/hide the button when hovering.
- trackingArea_.reset([[CrTrackingArea alloc]
- initWithRect:[self bounds]
- options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways
- owner:self
- userInfo:nil]);
- [self addTrackingArea:trackingArea_.get()];
+ [self setBordered:NO];
+
+ base::scoped_nsobject<CustomCircleImageCell> cell(
+ [[CustomCircleImageCell alloc] init]);
+ [self setCell:cell.get()];
+
+ [self setDefaultImage:CreateProfileImage(
+ profileIcon, kLargeImageSide).ToNSImage()];
+ [self setImagePosition:NSImageOnly];
NSRect bounds = NSMakeRect(0, 0, kLargeImageSide, kLargeImageSide);
if (editingAllowed) {
- changePhotoButton_.reset([self changePhotoButtonWithRect:bounds]);
- [self addSubview:changePhotoButton_];
-
- // Hide the button until the image is hovered over.
- [changePhotoButton_ setHidden:YES];
+ [self setTarget:self];
+ [self setAction:@selector(editPhoto:)];
+ changePhotoImage_.reset([[TransparentBackgroundImageView alloc]
+ initWithFrame:bounds]);
+ [changePhotoImage_ setImage:ui::ResourceBundle::GetSharedInstance().
+ GetNativeImageNamed(IDR_ICON_PROFILES_EDIT_CAMERA).AsNSImage()];
+ [self addSubview:changePhotoImage_];
+
+ // Hide the image until the button is hovered over.
+ [changePhotoImage_ setHidden:YES];
}
// Set the image cell's accessibility strings to be the same as the
return self;
}
-- (void)drawRect:(NSRect)dirtyRect {
- NSRect bounds = [self bounds];
-
- // Display the profile picture as a circle.
- NSBezierPath* path = [NSBezierPath bezierPathWithOvalInRect:bounds];
- [path addClip];
- [self.image drawAtPoint:bounds.origin
- fromRect:bounds
- operation:NSCompositeSourceOver
- fraction:1.0];
-
-}
-
- (void)editPhoto:(id)sender {
avatarMenu_->EditProfile(avatarMenu_->GetActiveProfileIndex());
[controller_
postActionPerformed:ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_IMAGE];
}
-- (void)mouseEntered:(NSEvent*)event {
- [changePhotoButton_ setHidden:NO];
-}
-
-- (void)mouseExited:(NSEvent*)event {
- [changePhotoButton_ setHidden:YES];
-}
-
-// Make sure the element is focusable for accessibility.
-- (BOOL)canBecomeKeyView {
- return YES;
+- (void)setHoverState:(HoverState)state {
+ [super setHoverState:state];
+ [changePhotoImage_ setHidden:([self hoverState] == kHoverStateNone)];
}
- (BOOL)accessibilityIsIgnored {
[super accessibilityPerformAction:action];
}
-- (TransparentBackgroundButton*)changePhotoButtonWithRect:(NSRect)rect {
- TransparentBackgroundButton* button =
- [[TransparentBackgroundButton alloc] initWithFrame:rect];
- [button setImage:ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
- IDR_ICON_PROFILES_EDIT_CAMERA).AsNSImage()];
- [button setImagePosition:NSImageOnly];
- [button setTarget:self];
- [button setAction:@selector(editPhoto:)];
- return button;
-}
@end
// A custom text control that turns into a textfield for editing when clicked.
}
@end
+// A custom dummy button that is used to clear focus from the bubble's controls.
+@interface DummyWindowFocusButton : NSButton
+@end
+
+@implementation DummyWindowFocusButton
+// Ignore accessibility, as this is a placeholder button.
+- (BOOL)accessibilityIsIgnored {
+ return YES;
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute {
+ return @[];
+}
+
+- (BOOL)canBecomeKeyView {
+ return false;
+}
+
+@end
+
@interface ProfileChooserController ()
// Builds the profile chooser view.
- (NSView*)buildProfileChooserView;
if (viewMode_ != profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER)
tutorialMode_ = profiles::TUTORIAL_MODE_NONE;
+ // Add a dummy, empty element so that we don't initially display any
+ // focus rings.
+ NSButton* dummyFocusButton =
+ [[[DummyWindowFocusButton alloc] initWithFrame:NSZeroRect] autorelease];
+ [dummyFocusButton setNextKeyView:subView];
+ [[self window] makeFirstResponder:dummyFocusButton];
+
[contentView addSubview:subView];
+ [contentView addSubview:dummyFocusButton];
SetWindowSize([self window],
NSMakeSize(NSWidth([subView frame]), NSHeight([subView frame])));
}
- (NSButton*)createOtherProfileView:(int)itemIndex {
const AvatarMenu::Item& item = avatarMenu_->GetItemAt(itemIndex);
- NSRect rect = NSMakeRect(0, 0, kFixedMenuWidth, kBlueButtonHeight);
+ NSRect rect = NSMakeRect(
+ 0, 0, kFixedMenuWidth, kBlueButtonHeight + kSmallVerticalSpacing);
base::scoped_nsobject<BackgroundColorHoverButton> profileButton(
[[BackgroundColorHoverButton alloc]
initWithFrame:rect
NSBox* separator = [self horizontalSeparatorWithFrame:
NSMakeRect(0, yOffset, kFixedGaiaViewWidth, 0)];
[container addSubview:separator];
- yOffset = NSMaxY([separator frame]) + kSmallVerticalSpacing;
+ yOffset = NSMaxY([separator frame]) + kVerticalSpacing;
NSView* titleView = BuildTitleCard(
NSMakeRect(0, yOffset, kFixedGaiaViewWidth, 0),
NSBox* separator = [self horizontalSeparatorWithFrame:
NSMakeRect(0, yOffset, kFixedAccountRemovalViewWidth, 0)];
[container addSubview:separator];
- yOffset = NSMaxY([separator frame]) + kSmallVerticalSpacing;
+ yOffset = NSMaxY([separator frame]) + kVerticalSpacing;
NSView* titleView = BuildTitleCard(
NSMakeRect(0, yOffset, kFixedAccountRemovalViewWidth,0),
yOffset = NSMaxY([disconnectButton frame]);
NSBox* separator = [self horizontalSeparatorWithFrame:
- NSMakeRect(0, yOffset, kFixedMenuWidth, 0)];
+ NSMakeRect(0, yOffset, kFixedSwitchUserViewWidth, 0)];
[container addSubview:separator];
yOffset = NSMaxY([separator frame]);
yOffset = NSMaxY([addPersonButton frame]);
separator = [self horizontalSeparatorWithFrame:
- NSMakeRect(0, yOffset, kFixedMenuWidth, 0)];
+ NSMakeRect(0, yOffset, kFixedSwitchUserViewWidth, 0)];
[container addSubview:separator];
yOffset = NSMaxY([separator frame]);
separator = [self horizontalSeparatorWithFrame:
NSMakeRect(0, yOffset, kFixedSwitchUserViewWidth, 0)];
[container addSubview:separator];
- yOffset = NSMaxY([separator frame]) + kSmallVerticalSpacing;
+ yOffset = NSMaxY([separator frame]) + kVerticalSpacing;
NSView* titleView = BuildTitleCard(
NSMakeRect(0, yOffset, kFixedSwitchUserViewWidth,0),
[container addSubview:titleView];
yOffset = NSMaxY([titleView frame]);
- [container setFrameSize:NSMakeSize(kFixedAccountRemovalViewWidth, yOffset)];
+ [container setFrameSize:NSMakeSize(kFixedSwitchUserViewWidth, yOffset)];
return container.autorelease();
}
StartProfileChooserController();
NSArray* subviews = [[[controller() window] contentView] subviews];
- ASSERT_EQ(1U, [subviews count]);
+ ASSERT_EQ(2U, [subviews count]);
subviews = [[subviews objectAtIndex:0] subviews];
// Three profiles means we should have one active card, one separator and
// Profile icon.
NSView* activeProfileImage = [activeCardSubviews objectAtIndex:2];
- EXPECT_TRUE([activeProfileImage isKindOfClass:[NSImageView class]]);
+ EXPECT_TRUE([activeProfileImage isKindOfClass:[NSButton class]]);
// Profile name.
NSView* activeProfileName = [activeCardSubviews objectAtIndex:1];
StartProfileChooserController();
NSArray* subviews = [[[controller() window] contentView] subviews];
- ASSERT_EQ(1U, [subviews count]);
+ ASSERT_EQ(2U, [subviews count]);
subviews = [[subviews objectAtIndex:0] subviews];
// Three profiles means we should have one active card and a
// Profile icon.
NSView* activeProfileImage = [activeCardSubviews objectAtIndex:2];
- EXPECT_TRUE([activeProfileImage isKindOfClass:[NSImageView class]]);
+ EXPECT_TRUE([activeProfileImage isKindOfClass:[NSButton class]]);
// Profile name.
NSView* activeProfileName = [activeCardSubviews objectAtIndex:1];
StartProfileChooserController();
NSArray* subviews = [[[controller() window] contentView] subviews];
- ASSERT_EQ(1U, [subviews count]);
+ ASSERT_EQ(2U, [subviews count]);
subviews = [[subviews objectAtIndex:0] subviews];
NSString* sortedNames[] = { @"Another Test",
@"New Profile",
switches::EnableNewAvatarMenuForTesting(CommandLine::ForCurrentProcess());
StartProfileChooserController();
NSArray* subviews = [[[controller() window] contentView] subviews];
- ASSERT_EQ(1U, [subviews count]);
+ ASSERT_EQ(2U, [subviews count]);
subviews = [[subviews objectAtIndex:0] subviews];
NSArray* activeCardSubviews = [[subviews objectAtIndex:2] subviews];
NSArray* activeCardLinks = [[activeCardSubviews objectAtIndex:0] subviews];
StartProfileChooserController();
NSArray* subviews = [[[controller() window] contentView] subviews];
- ASSERT_EQ(1U, [subviews count]);
+ ASSERT_EQ(2U, [subviews count]);
subviews = [[subviews objectAtIndex:0] subviews];
NSArray* activeCardSubviews = [[subviews objectAtIndex:2] subviews];
NSArray* activeCardLinks = [[activeCardSubviews objectAtIndex:0] subviews];
StartProfileChooserController();
NSArray* subviews = [[[controller() window] contentView] subviews];
- ASSERT_EQ(1U, [subviews count]);
+ ASSERT_EQ(2U, [subviews count]);
subviews = [[subviews objectAtIndex:0] subviews];
NSArray* activeCardSubviews = [[subviews objectAtIndex:2] subviews];
NSArray* activeCardLinks = [[activeCardSubviews objectAtIndex:0] subviews];
profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT];
NSArray* subviews = [[[controller() window] contentView] subviews];
- ASSERT_EQ(1U, [subviews count]);
+ ASSERT_EQ(2U, [subviews count]);
subviews = [[subviews objectAtIndex:0] subviews];
// There should be one active card, one accounts container, two separators
// Profile icon.
NSView* activeProfileImage = [activeCardSubviews objectAtIndex:2];
- EXPECT_TRUE([activeProfileImage isKindOfClass:[NSImageView class]]);
+ EXPECT_TRUE([activeProfileImage isKindOfClass:[NSButton class]]);
// Profile name.
NSView* activeProfileName = [activeCardSubviews objectAtIndex:1];
// Helper for performing tab selection as a result of dragging over a tab.
scoped_ptr<HoverTabSelector> hoverTabSelector_;
+
+ // A container view for the window controls, which must be manually added in
+ // fullscreen in 10.10+.
+ base::scoped_nsobject<NSView> fullscreenWindowControls_;
}
@property(nonatomic) CGFloat leftIndentForControls;
// Returns the currently active TabContentsController.
- (TabContentsController*)activeTabContentsController;
+// Adds traffic lights to the tab strip. Idempotent.
+- (void)addWindowControls;
+
+// Removes traffic lights from the tab strip. Idempotent.
+- (void)removeWindowControls;
+
@end
@interface TabStripController(TestingAPI)
return [tabContentsArray_ objectAtIndex:index];
}
+- (void)addWindowControls {
+ if (!fullscreenWindowControls_) {
+ // Make the container view.
+ CGFloat height = NSHeight([tabStripView_ frame]);
+ NSRect frame = NSMakeRect(0, 0, [self leftIndentForControls], height);
+ fullscreenWindowControls_.reset([[NSView alloc] initWithFrame:frame]);
+ [fullscreenWindowControls_
+ setAutoresizingMask:NSViewMaxXMargin | NSViewHeightSizable];
+
+ // Add the traffic light buttons. The horizontal layout was determined by
+ // manual inspection on Yosemite.
+ CGFloat closeButtonX = 11;
+ CGFloat miniButtonX = 31;
+ CGFloat zoomButtonX = 51;
+
+ NSUInteger styleMask = [[tabStripView_ window] styleMask];
+ NSButton* closeButton = [NSWindow standardWindowButton:NSWindowCloseButton
+ forStyleMask:styleMask];
+
+ // Vertically center the buttons in the tab strip.
+ CGFloat buttonY = floor((height - NSHeight([closeButton bounds])) / 2);
+ [closeButton setFrameOrigin:NSMakePoint(closeButtonX, buttonY)];
+ [fullscreenWindowControls_ addSubview:closeButton];
+
+ NSButton* miniaturizeButton =
+ [NSWindow standardWindowButton:NSWindowMiniaturizeButton
+ forStyleMask:styleMask];
+ [miniaturizeButton setFrameOrigin:NSMakePoint(miniButtonX, buttonY)];
+ [miniaturizeButton setEnabled:NO];
+ [fullscreenWindowControls_ addSubview:miniaturizeButton];
+
+ NSButton* zoomButton =
+ [NSWindow standardWindowButton:NSWindowZoomButton
+ forStyleMask:styleMask];
+ [fullscreenWindowControls_ addSubview:zoomButton];
+ [zoomButton setFrameOrigin:NSMakePoint(zoomButtonX, buttonY)];
+ }
+
+ if (![permanentSubviews_ containsObject:fullscreenWindowControls_]) {
+ [self addSubviewToPermanentList:fullscreenWindowControls_];
+ [self regenerateSubviewList];
+ }
+}
+
+- (void)removeWindowControls {
+ if (fullscreenWindowControls_)
+ [permanentSubviews_ removeObject:fullscreenWindowControls_];
+ [self regenerateSubviewList];
+}
+
- (void)themeDidChangeNotification:(NSNotification*)notification {
[self setNewTabImages];
}
namespace {
// Hardcoded value for the secure version of Acrobat Reader.
-const char kSecureVersion[] = "11.0.7.79";
+const char kSecureVersion[] = "11.0.8.4";
const char kAdobeReaderIdentifier[] = "adobe-reader";
const char kPdfMimeType[] = "application/pdf";
reader_info.is_enabled = plugin_status != PluginPrefs::POLICY_DISABLED;
}
- PluginMetadata::SecurityStatus security_status =
+ // Adobe Reader will likely always come up as "requires_authorization".
+ // See http://crbug.com/311655.
+ PluginMetadata::SecurityStatus security_stat =
plugin_metadata->GetSecurityStatus(plugins[i]);
reader_info.is_secure =
- security_status == PluginMetadata::SECURITY_STATUS_UP_TO_DATE;
+ security_stat == PluginMetadata::SECURITY_STATUS_UP_TO_DATE ||
+ security_stat == PluginMetadata::SECURITY_STATUS_REQUIRES_AUTHORIZATION;
reader_info.plugin_info = plugins[i];
break;
#include "chrome/browser/ui/views/apps/shaped_app_window_targeter.h"
#include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h"
#include "chrome/browser/ui/views/frame/taskbar_decorator.h"
+#include "chrome/browser/ui/zoom/zoom_controller.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/common/chrome_switches.h"
#include "extensions/common/extension.h"
arraysize(kAppWindowAcceleratorMap) +
arraysize(kAppWindowKioskAppModeAcceleratorMap));
+ // Ensure there is a ZoomController in kiosk mode, otherwise the processing
+ // of the accelerators will cause a crash.
+ DCHECK(!is_kiosk_app_mode ||
+ ZoomController::FromWebContents(web_view()->GetWebContents()));
+
for (std::map<ui::Accelerator, int>::const_iterator iter =
accelerator_table.begin();
iter != accelerator_table.end(); ++iter) {
// The amount of whitespace that is present when there is no padding. Used
// to get the proper spacing in the help section.
const int kHelpVerticalOffset = 5;
-const int kPasswordSectionHeight = 62;
// Wrapper around just the text portions of the generation UI (password and
// prompting text).
int height = kPopupBorderThickness;
if (controller_->display_password()) {
// Add divider height as well.
- height += kPasswordSectionHeight + 1;
+ height +=
+ PasswordGenerationPopupController::kPopupPasswordSectionHeight + 1;
}
int width = controller_->GetMinimumWidth();
int popup_width = width - 2 * kPopupBorderThickness;
// it), but it can't change the other way around.
CreatePasswordView();
password_view_->SetBounds(
- kPopupBorderThickness, y, popup_width, kPasswordSectionHeight);
+ kPopupBorderThickness,
+ y,
+ popup_width,
+ PasswordGenerationPopupController::kPopupPasswordSectionHeight);
divider_bounds_ =
gfx::Rect(kPopupBorderThickness, password_view_->bounds().bottom(),
popup_width, 1);
#include "chrome/browser/ui/webui/constrained_web_dialog_delegate_base.h"
#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/constrained_window_views.h"
+#include "chrome/browser/ui/webui/chrome_web_contents_handler.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/web_contents.h"
#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
#include "ui/web_dialogs/web_dialog_delegate.h"
#include "ui/web_dialogs/web_dialog_ui.h"
-#if defined(USE_AURA)
-#include "content/public/browser/render_widget_host_view.h"
-#include "ui/aura/client/focus_change_observer.h"
-#include "ui/aura/client/focus_client.h"
+namespace {
+
+class WebDialogWebContentsDelegateViews
+ : public ui::WebDialogWebContentsDelegate {
+ public:
+ WebDialogWebContentsDelegateViews(content::BrowserContext* browser_context,
+ content::WebContents* initiator,
+ views::WebView* web_view)
+ : ui::WebDialogWebContentsDelegate(browser_context,
+ new ChromeWebContentsHandler()),
+ initiator_(initiator),
+ web_view_(web_view) {
+ }
+ virtual ~WebDialogWebContentsDelegateViews() {}
+
+ // ui::WebDialogWebContentsDelegate:
+ virtual void WebContentsFocused(content::WebContents* contents) OVERRIDE {
+ // Ensure the WebView is focused when its WebContents is focused.
+ web_view_->RequestFocus();
+ }
+ virtual void HandleKeyboardEvent(
+ content::WebContents* source,
+ const content::NativeWebKeyboardEvent& event) OVERRIDE {
+ // Forward shortcut keys in dialog to the browser. http://crbug.com/104586
+ // Disabled on Mac due to http://crbug.com/112173
+#if !defined(OS_MACOSX)
+ Browser* current_browser = chrome::FindBrowserWithWebContents(initiator_);
+ if (!current_browser)
+ return;
+ current_browser->window()->HandleKeyboardEvent(event);
#endif
+ }
-using ui::WebDialogDelegate;
-using ui::WebDialogWebContentsDelegate;
+ private:
+ content::WebContents* initiator_;
+ views::WebView* web_view_;
-namespace {
+ DISALLOW_COPY_AND_ASSIGN(WebDialogWebContentsDelegateViews);
+};
class ConstrainedWebDialogDelegateViews
: public ConstrainedWebDialogDelegateBase {
public:
ConstrainedWebDialogDelegateViews(content::BrowserContext* context,
- WebDialogDelegate* delegate,
- WebDialogWebContentsDelegate* tab_delegate,
+ ui::WebDialogDelegate* delegate,
+ content::WebContents* web_contents,
views::WebView* view)
- : ConstrainedWebDialogDelegateBase(context, delegate, tab_delegate),
+ : ConstrainedWebDialogDelegateBase(context, delegate,
+ new WebDialogWebContentsDelegateViews(context, web_contents, view)),
view_(view) {}
virtual ~ConstrainedWebDialogDelegateViews() {}
- // WebDialogWebContentsDelegate:
+ // ui::WebDialogWebContentsDelegate:
virtual void CloseContents(content::WebContents* source) OVERRIDE {
view_->GetWidget()->Close();
}
DISALLOW_COPY_AND_ASSIGN(ConstrainedWebDialogDelegateViews);
};
-#if defined(USE_AURA)
-// TODO(msw): Make this part of WebView? Modify various WebContentsDelegates?
-class WebViewFocusHelper : public aura::client::FocusChangeObserver {
- public:
- explicit WebViewFocusHelper(views::WebView* web_view)
- : web_view_(web_view),
- window_(NULL),
- focus_client_(NULL) {
- if (web_view_ && web_view_->web_contents()) {
- content::RenderWidgetHostView* host_view =
- web_view_->web_contents()->GetRenderWidgetHostView();
- window_ = host_view ? host_view->GetNativeView() : NULL;
- }
- focus_client_ = window_ ? aura::client::GetFocusClient(window_) : NULL;
- if (focus_client_)
- focus_client_->AddObserver(this);
- }
-
- virtual ~WebViewFocusHelper() {
- if (focus_client_)
- focus_client_->RemoveObserver(this);
- }
-
- virtual void OnWindowFocused(aura::Window* gained_focus,
- aura::Window* lost_focus) OVERRIDE {
- if (gained_focus == window_ && !web_view_->HasFocus())
- web_view_->RequestFocus();
- }
-
- private:
- views::WebView* web_view_;
- aura::Window* window_;
- aura::client::FocusClient* focus_client_;
-
- DISALLOW_COPY_AND_ASSIGN(WebViewFocusHelper);
-};
-#endif
-
class ConstrainedWebDialogDelegateViewViews
: public views::WebView,
public ConstrainedWebDialogDelegate,
public:
ConstrainedWebDialogDelegateViewViews(
content::BrowserContext* browser_context,
- WebDialogDelegate* delegate,
- WebDialogWebContentsDelegate* tab_delegate)
+ ui::WebDialogDelegate* delegate,
+ content::WebContents* web_contents)
: views::WebView(browser_context),
impl_(new ConstrainedWebDialogDelegateViews(browser_context, delegate,
- tab_delegate, this)) {
+ web_contents, this)) {
SetWebContents(GetWebContents());
AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
}
virtual ~ConstrainedWebDialogDelegateViewViews() {}
// ConstrainedWebDialogDelegate:
- virtual const WebDialogDelegate* GetWebDialogDelegate() const OVERRIDE {
+ virtual const ui::WebDialogDelegate* GetWebDialogDelegate() const OVERRIDE {
return impl_->GetWebDialogDelegate();
}
- virtual WebDialogDelegate* GetWebDialogDelegate() OVERRIDE {
+ virtual ui::WebDialogDelegate* GetWebDialogDelegate() OVERRIDE {
return impl_->GetWebDialogDelegate();
}
virtual void OnDialogCloseFromWebUI() OVERRIDE {
return gfx::Size();
}
- void OnShow() {
-#if defined(USE_AURA)
- web_view_focus_helper_.reset(new WebViewFocusHelper(this));
-#endif
- }
-
private:
scoped_ptr<ConstrainedWebDialogDelegateViews> impl_;
-#if defined(USE_AURA)
- scoped_ptr<WebViewFocusHelper> web_view_focus_helper_;
-#endif
DISALLOW_COPY_AND_ASSIGN(ConstrainedWebDialogDelegateViewViews);
};
ConstrainedWebDialogDelegate* CreateConstrainedWebDialog(
content::BrowserContext* browser_context,
- WebDialogDelegate* delegate,
- WebDialogWebContentsDelegate* tab_delegate,
+ ui::WebDialogDelegate* delegate,
content::WebContents* web_contents) {
ConstrainedWebDialogDelegateViewViews* dialog =
new ConstrainedWebDialogDelegateViewViews(
- browser_context, delegate, tab_delegate);
+ browser_context, delegate, web_contents);
ShowWebModalDialogViews(dialog, web_contents);
- dialog->OnShow();
return dialog;
}
#include "chrome/grit/generated_resources.h"
#include "components/web_modal/popup_manager.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/event_constants.h"
DesktopMediaPickerDialogView::DesktopMediaPickerDialogView(
content::WebContents* parent_web_contents,
gfx::NativeWindow context,
- gfx::NativeWindow parent_window,
DesktopMediaPickerViews* parent,
const base::string16& app_name,
const base::string16& target_name,
GetMediaListViewHeightForRows(1), GetMediaListViewHeightForRows(2));
AddChildView(scroll_view_);
- // If |parent_web_contents| is set, the picker will be shown modal to the
- // web contents. Otherwise, a new dialog widget inside |parent_window| will be
- // created for the picker. Note that |parent_window| may also be NULL if
- // parent web contents is not set. In this case the picker will be parented
- // by a root window.
+ // If |parent_web_contents| is set and it's not a background page then the
+ // picker will be shown modal to the web contents. Otherwise the picker is
+ // shown in a separate window.
views::Widget* widget = NULL;
- if (parent_web_contents)
+ bool modal_dialog =
+ parent_web_contents &&
+ !parent_web_contents->GetDelegate()->IsNeverVisible(parent_web_contents);
+ if (modal_dialog) {
widget = CreateWebModalDialogViews(this, parent_web_contents);
- else
- widget = DialogDelegate::CreateDialogWidget(this, context, parent_window);
+ } else {
+ widget = DialogDelegate::CreateDialogWidget(this, context, NULL);
+ }
- // DesktopMediaList needs to know the ID of the picker window which
- // matches the ID it gets from the OS. Depending on the OS and configuration
- // we get this ID differently.
+ // If the picker is not modal to the calling web contents then it is displayed
+ // in its own top-level window, so in that case it needs to be filtered out of
+ // the list of top-level windows available for capture, and to achieve that
+ // the Id is passed to DesktopMediaList.
DesktopMediaID::Id dialog_window_id = 0;
-
- // If there is |parent_window| or |parent_web_contents|, the picker window
- // is embedded in the parent and does not have its own native window id, so we
- // do not filter in that case.
- if (!parent_window && !parent_web_contents) {
+ if (!modal_dialog) {
#if defined(USE_ASH)
if (chrome::IsNativeWindowInAsh(widget->GetNativeWindow())) {
dialog_window_id =
list_view_->StartUpdating(dialog_window_id);
- if (parent_web_contents) {
+ if (modal_dialog) {
web_modal::PopupManager* popup_manager =
web_modal::PopupManager::FromWebContents(parent_web_contents);
popup_manager->ShowModalDialog(GetWidget()->GetNativeView(),
const DoneCallback& done_callback) {
callback_ = done_callback;
dialog_ = new DesktopMediaPickerDialogView(
- web_contents, context, parent, this, app_name, target_name,
- media_list.Pass());
+ web_contents, context, this, app_name, target_name, media_list.Pass());
}
void DesktopMediaPickerViews::NotifyDialogResult(DesktopMediaID source) {
public:
DesktopMediaPickerDialogView(content::WebContents* parent_web_contents,
gfx::NativeWindow context,
- gfx::NativeWindow parent_window,
DesktopMediaPickerViews* parent,
const base::string16& app_name,
const base::string16& target_name,
picker_views_.reset(new DesktopMediaPickerViews());
picker_views_->Show(NULL,
- NULL,
parent_widget_->GetNativeWindow(),
+ NULL,
app_name,
app_name,
media_list.PassAs<DesktopMediaList>(),
// by 1 px.
const int kSearchButtonInset = 1;
-// Given a containing |height| and a |base_font_list|, shrinks the font size
-// until the font list will fit within |height| while having its cap height
-// vertically centered. Returns the correctly-sized font list.
-//
-// The expected layout:
-// +--------+-----------------------------------------------+------------+
-// | | y offset | space |
-// | +--------+-------------------+------------------+ above |
-// | | | | internal leading | cap height |
-// | box | font | ascent (baseline) +------------------+------------+
-// | height | height | | cap height |
-// | | |-------------------+------------------+------------+
-// | | | descent (height - baseline) | space |
-// | +--------+--------------------------------------+ below |
-// | | space at bottom | cap height |
-// +--------+-----------------------------------------------+------------+
-// Goal:
-// center of box height == center of cap height
-// (i.e. space above cap height == space below cap height)
-// Restrictions:
-// y offset >= 0
-// space at bottom >= 0
-// (i.e. Entire font must be visible inside the box.)
-gfx::FontList GetLargestFontListWithHeightBound(
- const gfx::FontList& base_font_list,
- int height) {
- gfx::FontList font_list = base_font_list;
- for (int font_size = font_list.GetFontSize(); font_size > 1; --font_size) {
- const int internal_leading =
- font_list.GetBaseline() - font_list.GetCapHeight();
- // Some platforms don't support getting the cap height, and simply return
- // the entire font ascent from GetCapHeight(). Centering the ascent makes
- // the font look too low, so if GetCapHeight() returns the ascent, center
- // the entire font height instead.
- const int space =
- height - ((internal_leading != 0) ?
- font_list.GetCapHeight() : font_list.GetHeight());
- const int y_offset = space / 2 - internal_leading;
- const int space_at_bottom = height - (y_offset + font_list.GetHeight());
- if ((y_offset >= 0) && (space_at_bottom >= 0))
- break;
- font_list = font_list.DeriveWithSizeDelta(-1);
- }
- return font_list;
-}
-
int GetEditLeadingInternalSpace() {
// The textfield has 1 px of whitespace before the text in the RTL case only.
return base::i18n::IsRTL() ? 1 : 0;
// Shrink large fonts to make them fit.
// TODO(pkasting): Stretch the location bar instead in this case.
const int location_height = GetInternalHeight(true);
- font_list = GetLargestFontListWithHeightBound(font_list, location_height);
+ font_list = font_list.DeriveWithHeightUpperBound(location_height);
// Determine the font for use inside the bubbles. The bubble background
// images have 1 px thick edges, which we don't want to overlap.
const int kBubbleInteriorVerticalPadding = 1;
const int bubble_vertical_padding =
(kBubblePadding + kBubbleInteriorVerticalPadding) * 2;
- const gfx::FontList bubble_font_list(
- GetLargestFontListWithHeightBound(
- font_list, location_height - bubble_vertical_padding));
+ const gfx::FontList bubble_font_list(font_list.DeriveWithHeightUpperBound(
+ location_height - bubble_vertical_padding));
const SkColor background_color =
GetColor(ToolbarModel::NONE, LocationBarView::BACKGROUND);
gfx::ShadowValue(gfx::Point(), 1.0f, SK_ColorDKGRAY)));
SetTextSubpixelRenderingEnabled(false);
+ // The largest text height that fits in the button. If the font list height
+ // is larger than this, it will be shrunk to match it.
+ // TODO(noms): Calculate this constant algorithmically.
+ const int kDisplayFontHeight = 15;
+ SetFontList(GetFontList().DeriveWithHeightUpperBound(kDisplayFontHeight));
+
ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
if (button_style == THEMED_BUTTON) {
const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_NORMAL);
UpdateAvatarButtonAndRelayoutParent();
}
+void NewAvatarButton::OnProfileAvatarChanged(
+ const base::FilePath& profile_path) {
+ UpdateAvatarButtonAndRelayoutParent();
+}
+
void NewAvatarButton::OnProfileSupervisedUserIdChanged(
const base::FilePath& profile_path) {
UpdateAvatarButtonAndRelayoutParent();
virtual void OnProfileNameChanged(
const base::FilePath& profile_path,
const base::string16& old_profile_name) OVERRIDE;
+ virtual void OnProfileAvatarChanged(
+ const base::FilePath& profile_path) OVERRIDE;
virtual void OnProfileSupervisedUserIdChanged(
const base::FilePath& profile_path) OVERRIDE;
const int kFixedGaiaViewHeight = 440;
const int kFixedGaiaViewWidth = 360;
const int kFixedAccountRemovalViewWidth = 280;
-const int kFixedSwitchUserViewWidth = 280;
+const int kFixedSwitchUserViewWidth = 320;
const int kLargeImageSide = 88;
+const int kVerticalSpacing = 16;
+
// Creates a GridLayout with a single column. This ensures that all the child
// views added get auto-expanded to fill the full width of the bubble.
views::GridLayout* CreateSingleColumnLayout(views::View* view, int width) {
AddChildView(title_label_);
}
- // Creates a new view that has the |title_card| with padding at the top, an
- // edge-to-edge separator below, and the specified |view| at the bottom.
+ // Creates a new view that has the |title_card| with horizontal padding at the
+ // top, an edge-to-edge separator below, and the specified |view| at the
+ // bottom.
static views::View* AddPaddedTitleCard(views::View* view,
TitleCard* title_card,
int width) {
layout->AddColumnSet(1)->AddColumn(views::GridLayout::FILL,
views::GridLayout::FILL, 0,views::GridLayout::FIXED, width, width);
- layout->StartRowWithPadding(1, 0, 0, views::kButtonVEdgeMarginNew);
+ layout->StartRowWithPadding(1, 0, 0, kVerticalSpacing);
layout->AddView(title_card);
- layout->StartRowWithPadding(1, 1, 0, views::kRelatedControlVerticalSpacing);
+ layout->StartRowWithPadding(1, 1, 0, kVerticalSpacing);
layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
layout->StartRow(1, 1);
// the Chrome menu.
if (!overflow_experiment) {
chevron_ = new ChevronMenuButton(NULL, base::string16(), this, false);
- chevron_->SetBorder(views::Border::NullBorder());
chevron_->EnableCanvasFlippingForRTLUI(true);
chevron_->SetAccessibleName(
l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS_CHEVRON));
gfx::NativeWindow parent) {
// TODO(bshe): UI tweaks needed for Aura HTML Dialog, such as adding padding
// on the title for Aura ConstrainedWebDialogUI.
- dialog_ = CreateConstrainedWebDialog(
- web_contents->GetBrowserContext(),
- this,
- NULL,
- web_contents);
+ dialog_ = CreateConstrainedWebDialog(web_contents->GetBrowserContext(), this,
+ web_contents);
}
NativeWebContentsModalDialog
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
+#include "base/sys_info.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
// Public methods.
void DemoModeDetector::InitDetection() {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableDemoMode))
+ return;
+
+ if (base::SysInfo::IsRunningOnChromeOS()) {
+ std::string track;
+ // We're running on an actual device; if we cannot find our release track
+ // value or if the track contains "testimage", don't start demo mode.
+ if (!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_TRACK", &track) ||
+ track.find("testimage") != std::string::npos)
+ return;
+ }
+
if (IsDerelict())
StartIdleDetection();
else
show_on_init_ = true;
return;
}
- if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableDemoMode))
- core_oobe_actor_->InitDemoModeDetection();
+ core_oobe_actor_->InitDemoModeDetection();
input_service_proxy_.AddObserver(this);
UpdateDevices();
return languages_list.Pass();
}
+std::string FindMostRelevantLocale(
+ const std::vector<std::string>& most_relevant_language_codes,
+ const base::ListValue& available_locales,
+ const std::string& fallback_locale) {
+ for (std::vector<std::string>::const_iterator most_relevant_it =
+ most_relevant_language_codes.begin();
+ most_relevant_it != most_relevant_language_codes.end();
+ ++most_relevant_it) {
+ for (base::ListValue::const_iterator available_it =
+ available_locales.begin();
+ available_it != available_locales.end(); ++available_it) {
+ base::DictionaryValue* dict;
+ std::string available_locale;
+ if (!(*available_it)->GetAsDictionary(&dict) ||
+ !dict->GetString("value", &available_locale)) {
+ NOTREACHED();
+ continue;
+ }
+ if (available_locale == *most_relevant_it)
+ return *most_relevant_it;
+ }
+ }
+
+ return fallback_locale;
+}
+
scoped_ptr<base::ListValue> GetAcceptLanguageList() {
// Collect the language codes from the supported accept-languages.
const std::string app_locale = g_browser_process->GetApplicationLocale();
// Resolve |locale| on a background thread, then continue on the current
// thread.
+ std::string (*get_application_locale)(const std::string&, bool) =
+ &l10n_util::GetApplicationLocale;
base::PostTaskAndReplyWithResult(
background_task_runner,
FROM_HERE,
- base::Bind(&l10n_util::GetApplicationLocale,
- locale),
- base::Bind(&GetKeyboardLayoutsForResolvedLocale,
- callback));
+ base::Bind(get_application_locale, locale, false /* set_icu_locale */),
+ base::Bind(&GetKeyboardLayoutsForResolvedLocale, callback));
}
scoped_ptr<base::DictionaryValue> GetCurrentKeyboardLayout() {
const std::vector<std::string>* most_relevant_language_codes,
const std::string& selected);
+// Returns the most first entry of |most_relevant_language_codes| that is
+// actually available (present in |available_locales|). If none of the entries
+// are present in |available_locales|, returns the |fallback_locale|.
+std::string FindMostRelevantLocale(
+ const std::vector<std::string>& most_relevant_language_codes,
+ const base::ListValue& available_locales,
+ const std::string& fallback_locale);
+
// Return a list of supported accept languages. The listed languages can be used
// in the Accept-Language header. The return value will look like:
// [{'code': 'fi', 'displayName': 'Finnish', 'nativeDisplayName': 'suomi'}, ...]
VerifyOnlyUILanguages(*list);
}
+TEST_F(L10nUtilTest, FindMostRelevantLocale) {
+ base::ListValue available_locales;
+ scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ dict->SetString("value", "de");
+ available_locales.Append(dict.release());
+ dict.reset(new base::DictionaryValue);
+ dict->SetString("value", "fr");
+ available_locales.Append(dict.release());
+ dict.reset(new base::DictionaryValue);
+ dict->SetString("value", "en-GB");
+ available_locales.Append(dict.release());
+
+ std::vector<std::string> most_relevant_language_codes;
+ EXPECT_EQ("en-US", FindMostRelevantLocale(most_relevant_language_codes,
+ available_locales,
+ "en-US"));
+
+ most_relevant_language_codes.push_back("xx");
+ EXPECT_EQ("en-US", FindMostRelevantLocale(most_relevant_language_codes,
+ available_locales,
+ "en-US"));
+
+ most_relevant_language_codes.push_back("fr");
+ EXPECT_EQ("fr", FindMostRelevantLocale(most_relevant_language_codes,
+ available_locales,
+ "en-US"));
+
+ most_relevant_language_codes.push_back("de");
+ EXPECT_EQ("fr", FindMostRelevantLocale(most_relevant_language_codes,
+ available_locales,
+ "en-US"));
+}
+
void InitStartupCustomizationDocumentForTesting(const std::string& manifest) {
StartupCustomizationDocument::GetInstance()->LoadManifestFromString(manifest);
StartupCustomizationDocument::GetInstance()->Init(
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/command_line.h"
#include "base/memory/weak_ptr.h"
#include "base/prefs/pref_service.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/sys_info.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
#include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
#include "chrome/common/pref_names.h"
-#include "chromeos/chromeos_switches.h"
#include "chromeos/ime/extension_ime_util.h"
#include "chromeos/network/network_handler.h"
#include "chromeos/network/network_state_handler.h"
true,
chromeos::network_handler::ErrorCallback());
ShowScreen(OobeUI::kScreenOobeNetwork, NULL);
-
- if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableDemoMode))
- return;
- if (base::SysInfo::IsRunningOnChromeOS()) {
- std::string track;
- // We're running on an actual device; if we cannot find our release track
- // value or if the track contains "testimage", don't start demo mode.
- if (!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_TRACK", &track) ||
- track.find("testimage") != std::string::npos)
- return;
- }
core_oobe_actor_->InitDemoModeDetection();
}
AddCallback("updateOfflineLogin",
&SigninScreenHandler::HandleUpdateOfflineLogin);
AddCallback("focusPod", &SigninScreenHandler::HandleFocusPod);
+ AddCallback("hardlockPod", &SigninScreenHandler::HandleHardlockPod);
AddCallback("retrieveAuthenticatedUserEmail",
&SigninScreenHandler::HandleRetrieveAuthenticatedUserEmail);
AddCallback("getPublicSessionKeyboardLayouts",
const std::string& username,
ScreenlockBridge::LockHandler::AuthType auth_type,
const base::string16& initial_value) {
+ if (delegate_->GetAuthType(username) ==
+ ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD)
+ return;
+
delegate_->SetAuthType(username, auth_type);
CallJS("login.AccountPickerScreen.setAuthType",
WallpaperManager::Get()->SetUserWallpaperDelayed(user_id);
}
+void SigninScreenHandler::HandleHardlockPod(const std::string& user_id) {
+ SetAuthType(user_id,
+ ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD,
+ base::string16());
+ HideUserPodCustomIcon(user_id);
+}
+
void SigninScreenHandler::HandleRetrieveAuthenticatedUserEmail(
double attempt_token) {
// TODO(antrim) : move GaiaSigninScreen dependency to GaiaSigninScreen.
void HandleUpdateOfflineLogin(bool offline_login_active);
void HandleShowSupervisedUserCreationScreen();
void HandleFocusPod(const std::string& user_id);
+ void HandleHardlockPod(const std::string& user_id);
void HandleLaunchKioskApp(const std::string& app_id, bool diagnostic_mode);
void HandleRetrieveAuthenticatedUserEmail(double attempt_token);
void HandleGetPublicSessionKeyboardLayouts(const std::string& user_id,
// |browser_context| is used to construct the constrained HTML dialog's
// WebContents.
// |delegate| controls the behavior of the dialog.
-// |tab_delegate| is optional, pass one in to use a custom
-// WebDialogWebContentsDelegate with the dialog, or NULL to
-// use the default one. The dialog takes ownership of
-// |tab_delegate|.
// |overshadowed| is the tab being overshadowed by the dialog.
ConstrainedWebDialogDelegate* CreateConstrainedWebDialog(
content::BrowserContext* browser_context,
ui::WebDialogDelegate* delegate,
- ui::WebDialogWebContentsDelegate* tab_delegate,
content::WebContents* overshadowed);
#endif // CHROME_BROWSER_UI_WEBUI_CONSTRAINED_WEB_DIALOG_UI_H_
ASSERT_TRUE(web_contents);
ConstrainedWebDialogDelegate* dialog_delegate =
- CreateConstrainedWebDialog(browser()->profile(),
- delegate,
- NULL,
- web_contents);
+ CreateConstrainedWebDialog(browser()->profile(), delegate, web_contents);
ASSERT_TRUE(dialog_delegate);
EXPECT_TRUE(dialog_delegate->GetNativeDialog());
EXPECT_TRUE(IsShowingWebContentsModalDialog(web_contents));
ASSERT_TRUE(web_contents);
ConstrainedWebDialogDelegate* dialog_delegate =
- CreateConstrainedWebDialog(browser()->profile(),
- delegate,
- NULL,
- web_contents);
+ CreateConstrainedWebDialog(browser()->profile(), delegate, web_contents);
ASSERT_TRUE(dialog_delegate);
scoped_ptr<WebContents> new_tab(dialog_delegate->GetWebContents());
ASSERT_TRUE(new_tab.get());
if (profile_index == std::string::npos)
return;
+ // Don't show the add/remove desktop shortcut button in the single user case.
+ if (cache.GetNumberOfProfiles() <= 1)
+ return;
+
const base::FilePath profile_path =
cache.GetPathOfProfileAtIndex(profile_index);
ProfileShortcutManager* shortcut_manager =
void ProfileSigninConfirmationDialog::Show(bool prompt) {
prompt_for_new_profile_ = prompt;
- dialog_delegate_ =
- CreateConstrainedWebDialog(profile_, this, NULL, web_contents_);
+ dialog_delegate_ = CreateConstrainedWebDialog(profile_, this, web_contents_);
}
ui::ModalType ProfileSigninConfirmationDialog::GetDialogModalType() const {
const std::string& user_email,
ScreenlockBridge::LockHandler::AuthType auth_type,
const base::string16& auth_value) {
+ if (GetAuthType(user_email) ==
+ ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD)
+ return;
+
user_auth_type_map_[user_email] = auth_type;
web_ui()->CallJavascriptFunction(
"login.AccountPickerScreen.setAuthType",
GetScreenlockRouter(email)->OnAuthAttempted(GetAuthType(email), "");
}
+void UserManagerScreenHandler::HandleHardlockUserPod(
+ const base::ListValue* args) {
+ std::string email;
+ CHECK(args->GetString(0, &email));
+ SetAuthType(email,
+ ScreenlockBridge::LockHandler::FORCE_OFFLINE_PASSWORD,
+ base::string16());
+ HideUserPodCustomIcon(email);
+}
+
void UserManagerScreenHandler::OnClientLoginSuccess(
const ClientLoginResult& result) {
chrome::SetLocalAuthCredentials(authenticating_profile_index_,
void HandleLaunchUser(const base::ListValue* args);
void HandleRemoveUser(const base::ListValue* args);
void HandleAttemptUnlock(const base::ListValue* args);
+ void HandleHardlockUserPod(const base::ListValue* args);
// Handle GAIA auth results.
virtual void OnClientLoginSuccess(const ClientLoginResult& result) OVERRIDE;
'../printing/printing.gyp:printing_java',
'../sync/sync.gyp:sync_java',
'../third_party/android_tools/android_tools.gyp:android_support_v7_appcompat_javalib',
+ '../third_party/android_tools/android_tools.gyp:android_support_v13_javalib',
'../third_party/guava/guava.gyp:guava_javalib',
'../ui/android/ui_android.gyp:ui_java',
],
widevine_cdm.is_out_of_process = true;
widevine_cdm.path = path;
widevine_cdm.name = kWidevineCdmDisplayName;
- widevine_cdm.description = kWidevineCdmDescription;
+ widevine_cdm.description = kWidevineCdmDescription +
+ std::string(" (version: ") +
+ WIDEVINE_CDM_VERSION_STRING + ")";
widevine_cdm.version = WIDEVINE_CDM_VERSION_STRING;
content::WebPluginMimeType widevine_cdm_mime_type(
kWidevineCdmPluginMimeType,
const char kActiveURL[] = "url-chunk";
+const char kFontKeyName[] = "font_key_name";
+
const char kSwitch[] = "switch-%" PRIuS;
const char kNumSwitches[] = "num-switches";
// base/:
{ "dm-usage", kSmallSize },
// content/:
+ { kFontKeyName, kSmallSize},
{ "ppapi_path", kMediumSize },
{ "subresource_url", kLargeSize },
#if defined(OS_CHROMEOS)
"1C93BD3CF875F4A73C0B2A163BB8FBDA8B8B3D80", // http://crbug.com/396117
"A3BC37E2148AC4E99BE4B16AF9D42DD1E592BBBE", // http://crbug.com/396117
"E703483CEF33DEC18B4B6DD84B5C776FB9182BDB", // http://crbug.com/396117
- "307E96539209F95A1A8740C713E6998A73657D96" // http://crbug.com/396117
+ "307E96539209F95A1A8740C713E6998A73657D96", // http://crbug.com/396117
+ "4F25792AF1AA7483936DE29C07806F203C7170A0", // http://crbug.com/407693
+ "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9", // http://crbug.com/407693
+ "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB", // http://crbug.com/407693
+ "81986D4F846CEDDDB962643FA501D1780DD441BB" // http://crbug.com/407693
]
}],
"browser_action": {
]
}
],
- "app.window.shape": {
- "channel": "stable",
- "extension_types": ["platform_app"]
- },
+ "app.window.shape": [
+ {
+ "channel": "dev",
+ "extension_types": ["platform_app"]
+ },
+ {
+ "channel": "stable",
+ "extension_types": ["platform_app"],
+ "whitelist": [
+ "0F42756099D914A026DADFA182871C015735DD95", // http://crbug.com/323773
+ "2D22CDB6583FD0A13758AEBE8B15E45208B4E9A7",
+ "E7E2461CE072DF036CF9592740196159E2D7C089", // http://crbug.com/356200
+ "A74A4D44C7CFCD8844830E6140C8D763E12DD8F3",
+ "312745D9BF916161191143F6490085EEA0434997",
+ "53041A2FA309EECED01FFC751E7399186E860B2C",
+ "EBA908206905323CECE6DC4B276A58A0F4AC573F",
+ "2775E568AC98F9578791F1EAB65A1BF5F8CEF414",
+ "4AA3C5D69A4AECBD236CAD7884502209F0F5C169",
+ "E410CDAB2C6E6DD408D731016CECF2444000A912",
+ "9E930B2B5EABA6243AE6C710F126E54688E8FAF6",
+ "FAFE8EFDD2D6AE2EEB277AFEB91C870C79064D9E", // http://crbug.com/327507
+ "3B52D273A271D4E2348233E322426DBAE854B567",
+ "5DF6ADC8708DF59FCFDDBF16AFBFB451380C2059",
+ "1037DEF5F6B06EA46153AD87B6C5C37440E3F2D1",
+ "F5815DAFEB8C53B078DD1853B2059E087C42F139",
+ "6A08EFFF9C16E090D6DCC7EC55A01CADAE840513",
+ "C32D6D93E12F5401DAA3A723E0C3CC5F25429BA4", // http://crbug.com/354258
+ "9099782647D39C778E15C8C6E0D23C88F5CDE170",
+ "B7D5B52D1E5B106288BD7F278CAFA5E8D76108B0",
+ "89349DBAA2C4022FB244AA50182AB60934EB41EE",
+ "CB593E510640572A995CB1B6D41BD85ED51E63F8",
+ "1AD1AC86C87969CD3434FA08D99DBA6840AEA612",
+ "9C2EA21D7975BDF2B3C01C3A454EE44854067A6D",
+ "D2C488C80C3C90C3E01A991112A05E37831E17D0",
+ "6EEC061C0E74B46C7B5BE2EEFA49436368F4988F",
+ "8B344D9E8A4C505EF82A0DBBC25B8BD1F984E777",
+ "E06AFCB1EB0EFD237824CC4AC8FDD3D43E8BC868",
+ "F76F43EFFF56BF17A9868A5243F339BA28746632", // http://crbug.com/386324
+ "C6EA52B92F80878515F94137020F01519357E5B5",
+ "E466389F058ABD73FF6FDD06F768A351FCBF8532",
+ "40063F1CF7B68BA847A26FA6620DDF156171D23C",
+ "A6FD8E15353CF1F5C3D0A7B20E1D10AEA4DD3E6A",
+ "57AC4D9E6BD8A2D0A70056B5FAC2378CAA588912",
+ "02037314DA4D913640DCF0E296A7D01F4FD793EC",
+ "B6EC0809BC63E10B431C5E4AA3645232CA86B2A5",
+ "48CA541313139786F056DBCB504A1025CFF5D2E3",
+ "05106136AE7F08A3C181D4648E5438350B1D2B4F"
+ ]
+ }
+ ],
"audio": [
{
"channel": "dev",
"E703483CEF33DEC18B4B6DD84B5C776FB9182BDB",
"A3BC37E2148AC4E99BE4B16AF9D42DD1E592BBBE", // http://crbug.com/335729
"1C93BD3CF875F4A73C0B2A163BB8FBDA8B8B3D80", // http://crbug.com/335729
- "307E96539209F95A1A8740C713E6998A73657D96" // http://crbug.com/335729
+ "307E96539209F95A1A8740C713E6998A73657D96", // http://crbug.com/335729
+ "4F25792AF1AA7483936DE29C07806F203C7170A0", // http://crbug.com/407693
+ "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9", // http://crbug.com/407693
+ "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB", // http://crbug.com/407693
+ "81986D4F846CEDDDB962643FA501D1780DD441BB" // http://crbug.com/407693
]
}
],
"4FE45FA56EF6A25FDE8C302C44045CA9CE8A605A", // http://crbug.com/320952
"3D14248405B8A59043420AAC160077C99E7788A9", // http://crbug.com/398585
"A6C87307BBE5886CC5F1393025000E2FE8060BF2", // http://crbug.com/398585
- "3407516021EA3669C0EC8E65E6B9837E5A521B9C" // http://crbug.com/398585
+ "3407516021EA3669C0EC8E65E6B9837E5A521B9C", // http://crbug.com/398585
+ "0F585FB1D0FDFBEBCE1FEB5E9DFFB6DA476B8C9B", // http://crbug.com/405800
+ "2D22CDB6583FD0A13758AEBE8B15E45208B4E9A7", // http://crbug.com/405800
+ "A07A5B743CD82A1C2579DB77D353C98A23201EEF", // http://crbug.com/405800
+ "0F42756099D914A026DADFA182871C015735DD95" // http://crbug.com/405800
]
}
],
"1C93BD3CF875F4A73C0B2A163BB8FBDA8B8B3D80", // http://crbug.com/387169
"A3BC37E2148AC4E99BE4B16AF9D42DD1E592BBBE", // http://crbug.com/387169
"E703483CEF33DEC18B4B6DD84B5C776FB9182BDB", // http://crbug.com/387169
- "307E96539209F95A1A8740C713E6998A73657D96" // http://crbug.com/387169
+ "307E96539209F95A1A8740C713E6998A73657D96", // http://crbug.com/387169
+ "4F25792AF1AA7483936DE29C07806F203C7170A0", // http://crbug.com/407693
+ "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9", // http://crbug.com/407693
+ "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB", // http://crbug.com/407693
+ "81986D4F846CEDDDB962643FA501D1780DD441BB" // http://crbug.com/407693
]
},
"bookmarkManagerPrivate": {
"FA01E0B81978950F2BC5A50512FD769725F57510", // Beta
"B11A93E7E5B541F8010245EBDE2C74647D6C14B9", // Canary
"F155646B5D1CA545F7E1E4E20D573DFDD44C2540", // Google Cast Beta
- "16CA7A47AAE4BE49B1E75A6B960C3875E945B264" // Google Cast Stable
+ "16CA7A47AAE4BE49B1E75A6B960C3875E945B264", // Google Cast Stable
+ "4F25792AF1AA7483936DE29C07806F203C7170A0", // http://crbug.com/407693
+ "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9", // http://crbug.com/407693
+ "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB", // http://crbug.com/407693
+ "81986D4F846CEDDDB962643FA501D1780DD441BB" // http://crbug.com/407693
]
},
"clipboardRead": {
"1C93BD3CF875F4A73C0B2A163BB8FBDA8B8B3D80", // http://crbug.com/389230
"A3BC37E2148AC4E99BE4B16AF9D42DD1E592BBBE", // http://crbug.com/389230
"E703483CEF33DEC18B4B6DD84B5C776FB9182BDB", // http://crbug.com/389230
- "307E96539209F95A1A8740C713E6998A73657D96" // http://crbug.com/389230
+ "307E96539209F95A1A8740C713E6998A73657D96", // http://crbug.com/389230
+ "4F25792AF1AA7483936DE29C07806F203C7170A0", // http://crbug.com/407693
+ "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9", // http://crbug.com/407693
+ "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB", // http://crbug.com/407693
+ "81986D4F846CEDDDB962643FA501D1780DD441BB" // http://crbug.com/407693
]
}
],
"A3BC37E2148AC4E99BE4B16AF9D42DD1E592BBBE", // http://crbug.com/293683
"8C3741E3AF0B93B6E8E0DDD499BB0B74839EA578", // http://crbug.com/234235
"E703483CEF33DEC18B4B6DD84B5C776FB9182BDB", // http://crbug.com/234235
- "307E96539209F95A1A8740C713E6998A73657D96" // http://crbug.com/329690
+ "307E96539209F95A1A8740C713E6998A73657D96", // http://crbug.com/329690
+ "4F25792AF1AA7483936DE29C07806F203C7170A0", // http://crbug.com/407693
+ "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9", // http://crbug.com/407693
+ "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB", // http://crbug.com/407693
+ "81986D4F846CEDDDB962643FA501D1780DD441BB" // http://crbug.com/407693
]
},
"fileBrowserHandler": {
"1C93BD3CF875F4A73C0B2A163BB8FBDA8B8B3D80", // http://crbug.com/389230
"A3BC37E2148AC4E99BE4B16AF9D42DD1E592BBBE", // http://crbug.com/389230
"E703483CEF33DEC18B4B6DD84B5C776FB9182BDB", // http://crbug.com/389230
- "307E96539209F95A1A8740C713E6998A73657D96" // http://crbug.com/389230
+ "307E96539209F95A1A8740C713E6998A73657D96", // http://crbug.com/389230
+ "4F25792AF1AA7483936DE29C07806F203C7170A0", // http://crbug.com/407693
+ "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9", // http://crbug.com/407693
+ "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB", // http://crbug.com/407693
+ "81986D4F846CEDDDB962643FA501D1780DD441BB" // http://crbug.com/407693
]
},
"location": [
"E703483CEF33DEC18B4B6DD84B5C776FB9182BDB", // http://crbug.com/234235
"307E96539209F95A1A8740C713E6998A73657D96", // http://crbug.com/329690
"A291B26E088FA6BA53FFD72F0916F06EBA7C585A", // http://crbug.com/341258
- "D7986543275120831B39EF28D1327552FC343960" // http://crbug.com/329088
+ "D7986543275120831B39EF28D1327552FC343960", // http://crbug.com/329088
+ "4F25792AF1AA7483936DE29C07806F203C7170A0", // http://crbug.com/407693
+ "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9", // http://crbug.com/407693
+ "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB", // http://crbug.com/407693
+ "81986D4F846CEDDDB962643FA501D1780DD441BB" // http://crbug.com/407693
]
},
"webcamPrivate": {
"16CA7A47AAE4BE49B1E75A6B960C3875E945B264", // Google Cast Stable
"7AE714FFD394E073F0294CFA134C9F91DB5FBAA4", // CCD Development
"C7DA3A55C2355F994D3FDDAD120B426A0DF63843", // CCD Testing
- "75E3CFFFC530582C583E4690EF97C70B9C8423B7" // CCD Release
+ "75E3CFFFC530582C583E4690EF97C70B9C8423B7", // CCD Release
+ "4F25792AF1AA7483936DE29C07806F203C7170A0", // http://crbug.com/407693
+ "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9", // http://crbug.com/407693
+ "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB", // http://crbug.com/407693
+ "81986D4F846CEDDDB962643FA501D1780DD441BB" // http://crbug.com/407693
]
},
"notificationProvider": {
// A phone eligible to unlock the device is detected, but it's not close
// enough to be allowed to unlock the device.
PHONE_NOT_NEARBY,
+ // A phone eligible to unlock the device is detected, but it is not allowed
+ // to unlock the device because it does not report its lock screen state.
+ PHONE_UNSUPPORTED,
// The devie can be unlocked using Easy Unlock.
AUTHENTICATED
};
static void seekBluetoothDeviceByAddress(DOMString deviceAddress,
optional EmptyCallback callback);
+ // Connects the socket to a remote Bluetooth device over an insecure
+ // connection, i.e. a connection that requests no bonding and no
+ // man-in-the-middle protection. Other than the reduced security setting,
+ // behaves identically to the chrome.bluetoothSocket.connect() function.
+ // |socketId|: The socket identifier, as issued by the
+ // chrome.bluetoothSocket API.
+ // |deviceAddress|: The Bluetooth address of the device to connect to.
+ // |uuid|: The UUID of the service to connect to.
+ // |callback|: Called when the connect attempt is complete.
+ static void connectToBluetoothServiceInsecurely(long socketId,
+ DOMString deviceAddress,
+ DOMString uuid,
+ EmptyCallback callback);
+
// Updates the screenlock state to reflect the Easy Unlock app state.
static void updateScreenlockState(State state,
optional EmptyCallback callback);
// TODO(hirono): Remove the property because of the design change of
// multi-profile suuport.
boolean isCurrentProfile;
-
- // Image set of profile image.
- ImageSet? profileImage;
};
// Mounted disk volume metadata.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Use <code>chrome.pushMessaging</code> to enable apps and extensions to
-// receive message data sent through
+// The <code>chrome.pushMessaging</code> API is deprecated since Chrome 38,
+// and will no longer be supported in Chrome 41.
+// Switch to <code>$(ref:gcm chrome.gcm)</code> to take advantage of
// <a href="cloudMessaging.html">Google Cloud Messaging</a>.
+[deprecated="Use $(ref:gcm chrome.gcm) API"]
namespace pushMessaging {
dictionary Message {
// to trigger push messages back to the app or extension.
// If the interactive flag is set, we will ask the user to log in
// when they are not already logged in.
+ [deprecated="Use $(ref:gcm chrome.gcm) API"]
static void getChannelId(optional boolean interactive,
ChannelIdCallback callback);
};
interface Events {
// Fired when a push message has been received.
// |message| : The details associated with the message.
+ [deprecated="Use $(ref:gcm chrome.gcm) API"]
static void onMessage(Message message);
};
};
chrome.desktopCapture.cancelChooseDesktopMedia(pending_request_id);
}
});
+
+document.querySelector('#startFromBackgroundPage')
+ .addEventListener('click', function(e) {
+ chrome.runtime.sendMessage(
+ {}, function(response) { console.log(response.farewell); });
+ });
}
});
});
+
+chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
+ chrome.desktopCapture.chooseDesktopMedia(
+ ["screen", "window"],
+ function(id) {
+ sendResponse({"id": id});
+ });
+});
</head>
<body>
<video id="video" autoplay></video>
- <p><button id="start">Start</button><button id="cancel">Cancel</button></p>
+ <p>
+ <button id="start">Start</button>
+ <button id="cancel">Cancel</button>
+ <button id="startFromBackgroundPage">Start from background page</button>
+ </p>
<script src="app.js"></script>
</body>
</html>
<h1>Google Cloud Messaging for Chrome V1</h1>
<p class="note">
-Google Cloud Messaging for Chrome V1 will be legacy soon.
-Use the new <a href="cloudMessaging">Google Cloud Messaging API</a>.
+Google Cloud Messaging for Chrome V1 is deprecated since Chrome 38.
+It will be removed in Chrome 41.
+Use the new <a href="cloudMessaging">Google Cloud Messaging</a>.
</p>
<p>
</ul>
</li>
<li>Set up your app or extension to use the service:
- <ul>
- <li>Add the permission to the manifest.</li>
- <li>Include a call to <code>getChannelId</code>
- for any user who is to receive a message.</li>
- <li>Register a handler to receive the
- <code>onMessage</code> event.</li>
- </ul>
- </li>
+ <ul>
+ <li>Add the permission to the manifest.</li>
+ <li>Include a call to <code>getChannelId</code>
+ for any user who is to receive a message.</li>
+ <li>Register a handler to receive the <code>onMessage</code> event.</li>
+ </ul>
+ </li>
<li>Publish your app in the Chrome Web Store. </li>
<li>Use refresh token to get a valid access token.</li>
<li>Send message to user.</li>
<meta name="doc-family" content="apps">
<h1>API Reference for GCM service</h1>
+<p class="note">
+This document explains sending messages to chrome.pushMessaging API, which is
+deprecated since Chrome 38. The API will be removed in Chrome 41.
+Use the new <a href="cloudMessaging">Google Cloud Messaging</a>.
+</p>
+
<p>
-The <a href="cloudMessaging">Google Cloud Messaging for Chrome</a> service
+The <a href="cloudMessagingV1">Google Cloud Messaging for Chrome</a> service
sends messages to users of a Chrome App.
The service handles all aspects of queueing and delivering messages.
To use the service,
</dict>
<key>^Versions/@VERSION_REGEX@/</key>
<dict>
- <key>nested</key>
- <true/>
<key>weight</key>
<real>20</real>
</dict>
<key>weight</key>
<real>30</real>
</dict>
+ <key>^Versions/@VERSION_REGEX@/.+/Resources/.+\.lproj/</key>
+ <dict>
+ <key>optional</key>
+ <true/>
+ <key>weight</key>
+ <real>30</real>
+ </dict>
+ <key>^Versions/@VERSION_REGEX@/.+/Resources Disabled/</key>
+ <dict>
+ <key>omit</key>
+ <true/>
+ <key>weight</key>
+ <real>40</real>
+ </dict>
<key>/\.DS_Store$</key>
<dict>
<key>omit</key>
const wchar_t* const kChannels[] = {
installer::kChromeChannelBeta,
- installer::kChromeChannelDev
+ installer::kChromeChannelDev,
+ installer::kChromeChannelStableExplicit
};
const wchar_t* const kModifiers[] = {
*const* end = &kChannels[arraysize(kChannels)]; scan != end;
++scan) {
if (value_.find(*scan) != std::wstring::npos) {
- channel_name->assign(*scan);
+ // Report channels with "stable" in them as stable (empty string).
+ if (*scan == installer::kChromeChannelStableExplicit)
+ channel_name->erase();
+ else
+ channel_name->assign(*scan);
return true;
}
}
EXPECT_TRUE(ci.GetChannelName(&channel));
EXPECT_EQ(kChannelDev, channel);
+ ci.set_value(L"x64-dev");
+ EXPECT_TRUE(ci.GetChannelName(&channel));
+ EXPECT_EQ(kChannelDev, channel);
+ ci.set_value(L"x64-beta");
+ EXPECT_TRUE(ci.GetChannelName(&channel));
+ EXPECT_EQ(kChannelBeta, channel);
+ ci.set_value(L"x64-stable");
+ EXPECT_TRUE(ci.GetChannelName(&channel));
+ EXPECT_EQ(kChannelStable, channel);
+
ci.set_value(L"fuzzy");
EXPECT_FALSE(ci.GetChannelName(&channel));
}
const wchar_t kChromeChannelDev[] = L"dev";
const wchar_t kChromeChannelBeta[] = L"beta";
const wchar_t kChromeChannelStable[] = L"";
+const wchar_t kChromeChannelStableExplicit[] = L"stable";
const size_t kMaxAppModelIdLength = 64U;
extern const wchar_t kChromeChannelDev[];
extern const wchar_t kChromeChannelBeta[];
extern const wchar_t kChromeChannelStable[];
+extern const wchar_t kChromeChannelStableExplicit[];
extern const size_t kMaxAppModelIdLength;
include_rules = [
"+chrome/grit", # For generated headers.
+ "+sandbox/win/src",
]
#include "content/public/common/sandbox_init.h"
#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "printing/emf_win.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
namespace {
*exposed_dir = exposed_dir_;
}
+ virtual void PreSpawnTarget(sandbox::TargetPolicy* policy,
+ bool* success) OVERRIDE {
+ // Service process may run as windows service and it fails to create a
+ // window station.
+ policy->SetAlternateDesktop(false);
+ }
+
private:
base::FilePath exposed_dir_;
};
SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST,
SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED,
SERVICE_UTILITY_SEMANTIC_CAPS_FAILED,
+ SERVICE_UTILITY_FAILED_TO_START,
SERVICE_UTILITY_EVENT_MAX,
};
} // namespace
SERVICE_UTILITY_EVENT_MAX);
return true;
}
+ UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
+ SERVICE_UTILITY_FAILED_TO_START,
+ SERVICE_UTILITY_EVENT_MAX);
return false;
}
});
}
-testcase.multiProfileBadge = function() {
- var appId;
- StepsRunner.run([
- function() {
- setupAndWaitUntilReady(null, RootPath.DOWNLOADS, this.next);
- },
- // Add all users.
- function(inAppId) {
- appId = inAppId;
- chrome.test.sendMessage(JSON.stringify({name: 'addAllUsers'}),
- this.next);
- },
- // Get the badge element.
- function(json) {
- chrome.test.assertTrue(JSON.parse(json));
- waitForElement(appId, '#profile-badge').then(this.next);
- },
- // Verify no badge image is shown yet. Move to other deskop.
- function(element) {
- chrome.test.assertTrue(element.hidden, 'Badge hidden initially');
- callRemoteTestUtil('visitDesktop',
- appId,
- ['bob@invalid.domain'],
- this.next);
- },
- // Get the badge element again.
- function(result) {
- chrome.test.assertTrue(result);
- waitForElement(appId, '#profile-badge:not([hidden])').then(this.next);
- },
- // Verify an image source is filled. Go back to the original desktop
- function(element) {
- callRemoteTestUtil('queryAllElements',
- appId,
- ['#profile-badge',
- null,
- ['background']]).then(this.next);
- },
- function(elements) {
- chrome.test.assertTrue(
- elements[0].styles.background.indexOf('data:image') !== -1,
- 'Badge shown after moving desktop');
- callRemoteTestUtil('visitDesktop',
- appId,
- ['alice@invalid.domain'],
- this.next);
- },
- // Wait for #profile-badge element to disappear.
- function(result) {
- chrome.test.assertTrue(result);
- waitForElementLost(appId, '#profile-badge:not([hidden])').then(this.next);
- },
- // The image is gone.
- function(result) {
- checkIfNoErrorsOccured(this.next);
- }
- ]);
-};
-
testcase.multiProfileVisitDesktopMenu = function() {
var appId;
StepsRunner.run([
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/nacl/nacl_browsertest_util.h"
+#include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/browser/web_contents.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
+#include "net/url_request/url_request.h"
using net::test_server::BasicHttpResponse;
using net::test_server::EmbeddedTestServer;
using net::test_server::HttpRequest;
using net::test_server::HttpResponse;
+void TestDispatcherHostDelegate::RequestBeginning(
+ net::URLRequest* request,
+ content::ResourceContext* resource_context,
+ content::AppCacheService* appcache_service,
+ content::ResourceType resource_type,
+ int child_id,
+ int route_id,
+ ScopedVector<content::ResourceThrottle>* throttles) {
+ // This checks the same condition as the one for PNaCl in
+ // AppendComponentUpdaterThrottles.
+ if (resource_type == content::RESOURCE_TYPE_OBJECT) {
+ const net::HttpRequestHeaders& headers = request->extra_request_headers();
+ std::string accept_headers;
+ if (headers.GetHeader("Accept", &accept_headers)) {
+ if (accept_headers.find("application/x-pnacl") != std::string::npos)
+ found_pnacl_header_ = true;
+ }
+ }
+}
+
PnaclHeaderTest::PnaclHeaderTest() : noncors_loads_(0), cors_loads_(0) {}
PnaclHeaderTest::~PnaclHeaderTest() {}
void PnaclHeaderTest::RunLoadTest(const std::string& url,
int expected_noncors,
int expected_cors) {
+ content::ResourceDispatcherHost::Get()->SetDelegate(&test_delegate_);
StartServer();
LoadTestMessageHandler handler;
content::JavascriptTestObserver observer(
base::ScopedPathOverride component_dir(chrome::DIR_PNACL_COMPONENT);
ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL(url));
+
// Wait until the NMF and pexe are also loaded, not just the HTML.
// Do this by waiting till the LoadTestMessageHandler responds.
EXPECT_TRUE(observer.Run()) << handler.error_message();
+
+ // Now check the expectations.
EXPECT_TRUE(handler.test_passed()) << "Test failed.";
EXPECT_EQ(expected_noncors, noncors_loads_);
EXPECT_EQ(expected_cors, cors_loads_);
+
+ content::ResourceDispatcherHost::Get()->SetDelegate(NULL);
}
scoped_ptr<HttpResponse> PnaclHeaderTest::WatchForPexeFetch(
if (absolute_url.path().find(".pexe") == std::string::npos)
return scoped_ptr<HttpResponse>();
- // For pexe files, check for the special Accept header.
- // This must match whatever is in:
- // ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc
+ // For pexe files, check for the special Accept header,
+ // along with the expected ResourceType of the URL request.
EXPECT_NE(0U, request.headers.count("Accept"));
std::map<std::string, std::string>::const_iterator it =
request.headers.find("Accept");
EXPECT_NE(std::string::npos, it->second.find("application/x-pnacl"));
EXPECT_NE(std::string::npos, it->second.find("*/*"));
+ EXPECT_TRUE(test_delegate_.found_pnacl_header());
// Also make sure that other headers like CORS-related headers
// are preserved when injecting the special Accept header.
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/resource_dispatcher_host_delegate.h"
+#include "content/public/common/resource_type.h"
namespace base {
class FilePath;
}
}
+using content::ResourceDispatcherHostDelegate;
+
+class TestDispatcherHostDelegate : public ResourceDispatcherHostDelegate {
+ public:
+ explicit TestDispatcherHostDelegate()
+ : ResourceDispatcherHostDelegate(), found_pnacl_header_(false) {}
+
+ virtual ~TestDispatcherHostDelegate() {}
+
+ virtual void RequestBeginning(
+ net::URLRequest* request,
+ content::ResourceContext* resource_context,
+ content::AppCacheService* appcache_service,
+ content::ResourceType resource_type,
+ int child_id,
+ int route_id,
+ ScopedVector<content::ResourceThrottle>* throttles) OVERRIDE;
+
+ bool found_pnacl_header() const { return found_pnacl_header_; }
+
+ private:
+ bool found_pnacl_header_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDispatcherHostDelegate);
+};
+
class PnaclHeaderTest : public InProcessBrowserTest {
public:
PnaclHeaderTest();
int noncors_loads_;
int cors_loads_;
+ TestDispatcherHostDelegate test_delegate_;
DISALLOW_COPY_AND_ASSIGN(PnaclHeaderTest);
};
// See crbug.com/379218.
L"activedetect64.dll", // Lenovo One Key Theater.
L"bitguard.dll", // Unknown (suspected malware).
- L"cespy.dll", // CovenantEyes.
L"chrmxtn.dll", // Unknown (keystroke logger).
L"cplushook.dll", // Unknown (suspected malware).
L"datamngr.dll", // Unknown (suspected adware).
namespace chromeos {
FakeSessionManagerClient::FakeSessionManagerClient()
- : start_device_wipe_call_count_(0),
+ : first_boot_(false),
+ start_device_wipe_call_count_(0),
notify_lock_screen_shown_call_count_(0),
notify_lock_screen_dismissed_call_count_(0) {
}
void FakeSessionManagerClient::GetServerBackedStateKeys(
const StateKeysCallback& callback) {
base::MessageLoop::current()->PostTask(
- FROM_HERE, base::Bind(callback, server_backed_state_keys_));
+ FROM_HERE, base::Bind(callback, server_backed_state_keys_, first_boot_));
}
const std::string& FakeSessionManagerClient::device_policy() const {
server_backed_state_keys_ = state_keys;
}
+ void set_first_boot(bool first_boot) { first_boot_ = first_boot; }
+
int start_device_wipe_call_count() const {
return start_device_wipe_call_count_;
}
ObserverList<Observer> observers_;
SessionManagerClient::ActiveSessionsMap user_sessions_;
std::vector<std::string> server_backed_state_keys_;
+ bool first_boot_;
int start_device_wipe_call_count_;
int notify_lock_screen_shown_call_count_;
void OnGetServerBackedStateKeys(const StateKeysCallback& callback,
dbus::Response* response) {
std::vector<std::string> state_keys;
+ bool first_run = false;
if (!response) {
LOG(ERROR) << "Failed to call "
<< login_manager::kSessionManagerStartSession;
std::string(reinterpret_cast<const char*>(data), size));
}
}
+ if (!reader.PopBool(&first_run)) {
+ // TODO(tnagel): After 2014-11-19 turn this warning into an error.
+ LOG(WARNING) << "Chrome OS is too old. Defaulting to first_run=false.";
+ }
}
if (!callback.is_null())
- callback.Run(state_keys);
+ callback.Run(state_keys, first_run);
}
state_keys.push_back(crypto::SHA256HashString(base::IntToString(i)));
if (!callback.is_null())
- callback.Run(state_keys);
+ callback.Run(state_keys, false);
}
private:
virtual void SetFlagsForUser(const std::string& username,
const std::vector<std::string>& flags) = 0;
- typedef base::Callback<void(const std::vector<std::string>& state_keys)>
- StateKeysCallback;
+ typedef base::Callback<void(const std::vector<std::string>& state_keys,
+ bool first_boot)> StateKeysCallback;
// Get the currently valid server-backed state keys for the device.
// Server-backed state keys are opaque, device-unique, time-dependent,
// client-determined identifiers that are used for keying state in the cloud
- // for the device to retrieve after a device factory reset.
+ // for the device to retrieve after a device factory reset. The |first_boot|
+ // parameter indicates if this is the very first boot of the device after
+ // being assembled (even a "factory reset" will not trigger this again) in
+ // which case doing the enrollment check makes no sense.
//
// The state keys are returned asynchronously via |callback|. The callback
// will be invoked with an empty state key vector in case of errors.
// updated to match.
InsertDataListValues(&values, &labels, &icons, &ids);
+// Temporarily disabled. See http://crbug.com/408695
+#if 0
#if defined(OS_MACOSX) && !defined(OS_IOS)
if (values.empty() &&
manager_->ShouldShowAccessAddressBookSuggestion(query_form_,
EmitHistogram(SHOWED_ACCESS_ADDRESS_BOOK_ENTRY);
}
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+#endif
if (values.empty()) {
// No suggestions, any popup currently showing is obsolete.
AutofillField* field = submitted_form->field(i);
ServerFieldTypeSet matching_types;
- // If it's a password field, set the type directly.
- if (field->form_control_type == "password") {
- matching_types.insert(PASSWORD);
- } else {
- base::string16 value;
- base::TrimWhitespace(field->value, base::TRIM_ALL, &value);
- for (std::vector<AutofillProfile>::const_iterator it = profiles.begin();
- it != profiles.end(); ++it) {
- it->GetMatchingTypes(value, app_locale, &matching_types);
- }
- for (std::vector<CreditCard>::const_iterator it = credit_cards.begin();
- it != credit_cards.end(); ++it) {
- it->GetMatchingTypes(value, app_locale, &matching_types);
- }
+ base::string16 value;
+ base::TrimWhitespace(field->value, base::TRIM_ALL, &value);
+ for (std::vector<AutofillProfile>::const_iterator it = profiles.begin();
+ it != profiles.end(); ++it) {
+ it->GetMatchingTypes(value, app_locale, &matching_types);
+ }
+ for (std::vector<CreditCard>::const_iterator it = credit_cards.begin();
+ it != credit_cards.end(); ++it) {
+ it->GetMatchingTypes(value, app_locale, &matching_types);
}
if (matching_types.empty())
ServerFieldTypeSet non_empty_types;
personal_data_->GetNonEmptyTypes(&non_empty_types);
- // Always add PASSWORD to |non_empty_types| so that if |submitted_form|
- // contains a password field it will be uploaded to the server. If
- // |submitted_form| doesn't contain a password field, there is no side
- // effect from adding PASSWORD to |non_empty_types|.
- non_empty_types.insert(PASSWORD);
download_manager_->StartUploadRequest(submitted_form, was_autofilled,
non_empty_types);
}
-bool AutofillManager::UploadPasswordGenerationForm(const FormData& form) {
+bool AutofillManager::UploadPasswordForm(
+ const FormData& form,
+ const ServerFieldType& password_type) {
FormStructure form_structure(form);
if (!ShouldUploadForm(form_structure))
if (!form_structure.ShouldBeCrowdsourced())
return false;
- // TODO(gcasto): Check that PasswordGeneration is enabled?
-
// Find the first password field to label. We don't try to label anything
// else.
bool found_password_field = false;
ServerFieldTypeSet types;
if (!found_password_field && field->form_control_type == "password") {
- types.insert(ACCOUNT_CREATION_PASSWORD);
+ types.insert(password_type);
found_password_field = true;
} else {
types.insert(UNKNOWN_TYPE);
// Only one field type should be present.
ServerFieldTypeSet available_field_types;
- available_field_types.insert(ACCOUNT_CREATION_PASSWORD);
+ available_field_types.insert(password_type);
// Force uploading as these events are relatively rare and we want to make
// sure to receive them. It also makes testing easier if these requests
void OnSetDataList(const std::vector<base::string16>& values,
const std::vector<base::string16>& labels);
- // Try and upload |form|. This differs from OnFormSubmitted() in a few ways.
+ // Try to label password fields and upload |form|. This differs from
+ // OnFormSubmitted() in a few ways.
// - This function will only label the first <input type="password"> field
- // as ACCOUNT_CREATION_PASSWORD. Other fields will stay unlabeled, as they
+ // as |password_type|. Other fields will stay unlabeled, as they
// should have been labeled during the upload for OnFormSubmitted().
// - This function does not assume that |form| is being uploaded during
// the same browsing session as it was originally submitted (as we may
// not have the necessary information to classify the form at that time)
// so it bypasses the cache and doesn't log the same quality UMA metrics.
- bool UploadPasswordGenerationForm(const FormData& form);
+ bool UploadPasswordForm(const FormData& form,
+ const ServerFieldType& pasword_type);
// Resets cache.
virtual void Reset();
namespace {
-const int kPickleVersion = 1;
+const int kPickleVersion = 2;
bool ReadGURL(PickleIterator* iter, GURL* url) {
std::string spec;
return true;
}
+void LogDeserializationError(int version) {
+ DVLOG(1) << "Could not deserialize version " << version
+ << " FormData from pickle.";
+}
+
} // namespace
FormData::FormData()
bool DeserializeFormData(PickleIterator* iter, FormData* form_data) {
int version;
if (!iter->ReadInt(&version)) {
- LOG(ERROR) << "Bad pickle of FormData, no version present";
+ DVLOG(1) << "Bad pickle of FormData, no version present";
return false;
}
switch (version) {
case 1: {
+ base::string16 method;
if (!iter->ReadString16(&form_data->name) ||
+ !iter->ReadString16(&method) ||
!ReadGURL(iter, &form_data->origin) ||
!ReadGURL(iter, &form_data->action) ||
!iter->ReadBool(&form_data->user_submitted) ||
!DeserializeFormFieldDataVector(iter, &form_data->fields)) {
- LOG(ERROR) << "Could not deserialize FormData from pickle";
+ LogDeserializationError(version);
return false;
}
break;
}
+ case 2:
+ if (!iter->ReadString16(&form_data->name) ||
+ !ReadGURL(iter, &form_data->origin) ||
+ !ReadGURL(iter, &form_data->action) ||
+ !iter->ReadBool(&form_data->user_submitted) ||
+ !DeserializeFormFieldDataVector(iter, &form_data->fields)) {
+ LogDeserializationError(version);
+ return false;
+ }
+ break;
default: {
- LOG(ERROR) << "Unknown FormData pickle version " << version;
+ DVLOG(1) << "Unknown FormData pickle version " << version;
return false;
}
}
#include "base/pickle.h"
#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/form_field_data.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace {
+
+// This function serializes the form data into the pickle in version one format.
+// It should always be possible to deserialize it using DeserializeFormData(),
+// even when version changes. See kPickleVersion in form_data.cc.
+void SerializeInVersion1Format(const autofill::FormData& form_data,
+ Pickle* pickle) {
+ pickle->WriteInt(1);
+ pickle->WriteString16(form_data.name);
+ base::string16 method(base::ASCIIToUTF16("POST"));
+ pickle->WriteString16(method);
+ pickle->WriteString(form_data.origin.spec());
+ pickle->WriteString(form_data.action.spec());
+ pickle->WriteBool(form_data.user_submitted);
+ pickle->WriteInt(static_cast<int>(form_data.fields.size()));
+ for (size_t i = 0; i < form_data.fields.size(); ++i) {
+ SerializeFormFieldData(form_data.fields[i], pickle);
+ }
+}
+
+// This function serializes the form data into the pickle in incorrect format
+// (no version number).
+void SerializeIncorrectFormat(const autofill::FormData& form_data,
+ Pickle* pickle) {
+ pickle->WriteString16(form_data.name);
+ pickle->WriteString(form_data.origin.spec());
+ pickle->WriteString(form_data.action.spec());
+ pickle->WriteBool(form_data.user_submitted);
+ pickle->WriteInt(static_cast<int>(form_data.fields.size()));
+ for (size_t i = 0; i < form_data.fields.size(); ++i) {
+ SerializeFormFieldData(form_data.fields[i], pickle);
+ }
+}
+
+}
+
namespace autofill {
TEST(FormDataTest, SerializeAndDeserialize) {
EXPECT_EQ(actual, data);
}
+TEST(FormDataTest, Serialize_v1_Deserialize_vCurrent) {
+ FormData data;
+ data.name = base::ASCIIToUTF16("name");
+ data.origin = GURL("origin");
+ data.action = GURL("action");
+ data.user_submitted = true;
+
+ FormFieldData field_data;
+ field_data.label = base::ASCIIToUTF16("label");
+ field_data.name = base::ASCIIToUTF16("name");
+ field_data.value = base::ASCIIToUTF16("value");
+ field_data.form_control_type = "password";
+ field_data.autocomplete_attribute = "off";
+ field_data.max_length = 200;
+ field_data.is_autofilled = true;
+ field_data.is_checked = true;
+ field_data.is_checkable = true;
+ field_data.is_focusable = true;
+ field_data.should_autocomplete = false;
+ field_data.text_direction = base::i18n::RIGHT_TO_LEFT;
+ field_data.option_values.push_back(base::ASCIIToUTF16("First"));
+ field_data.option_values.push_back(base::ASCIIToUTF16("Second"));
+ field_data.option_contents.push_back(base::ASCIIToUTF16("First"));
+ field_data.option_contents.push_back(base::ASCIIToUTF16("Second"));
+
+ data.fields.push_back(field_data);
+
+ // Change a few fields.
+ field_data.max_length = 150;
+ field_data.option_values.push_back(base::ASCIIToUTF16("Third"));
+ field_data.option_contents.push_back(base::ASCIIToUTF16("Third"));
+ data.fields.push_back(field_data);
+
+ Pickle pickle;
+ SerializeInVersion1Format(data, &pickle);
+
+ PickleIterator iter(pickle);
+ FormData actual;
+ EXPECT_TRUE(DeserializeFormData(&iter, &actual));
+
+ EXPECT_EQ(actual, data);
+}
+
+TEST(FormDataTest, SerializeIncorrectFormatAndDeserialize) {
+ FormData data;
+ data.name = base::ASCIIToUTF16("name");
+ data.origin = GURL("origin");
+ data.action = GURL("action");
+ data.user_submitted = true;
+
+ FormFieldData field_data;
+ field_data.label = base::ASCIIToUTF16("label");
+ field_data.name = base::ASCIIToUTF16("name");
+ field_data.value = base::ASCIIToUTF16("value");
+ field_data.form_control_type = "password";
+ field_data.autocomplete_attribute = "off";
+ field_data.max_length = 200;
+ field_data.is_autofilled = true;
+ field_data.is_checked = true;
+ field_data.is_checkable = true;
+ field_data.is_focusable = true;
+ field_data.should_autocomplete = false;
+ field_data.text_direction = base::i18n::RIGHT_TO_LEFT;
+ field_data.option_values.push_back(base::ASCIIToUTF16("First"));
+ field_data.option_values.push_back(base::ASCIIToUTF16("Second"));
+ field_data.option_contents.push_back(base::ASCIIToUTF16("First"));
+ field_data.option_contents.push_back(base::ASCIIToUTF16("Second"));
+
+ data.fields.push_back(field_data);
+
+ // Change a few fields.
+ field_data.max_length = 150;
+ field_data.option_values.push_back(base::ASCIIToUTF16("Third"));
+ field_data.option_contents.push_back(base::ASCIIToUTF16("Third"));
+ data.fields.push_back(field_data);
+
+ Pickle pickle;
+ SerializeIncorrectFormat(data, &pickle);
+
+ PickleIterator iter(pickle);
+ FormData actual;
+ EXPECT_FALSE(DeserializeFormData(&iter, &actual));
+}
+
} // namespace autofill
signals->set_observed_time_millis(base::Time::Now().ToJsTime());
}
-OptInStateFilter* CreateOptedInOrOutFilter() {
- OptInStateFilter* filter = new OptInStateFilter;
- filter->add_allowed_opt_in_state(copresence::OPTED_IN);
- filter->add_allowed_opt_in_state(copresence::OPTED_OUT);
- return filter;
-}
-
-void AllowOptedOutMessages(ReportRequest* request) {
- // TODO(ckehoe): Collapse this pattern into ProcessPublish()
- // and ProcessSubscribe() methods.
-
- if (request->has_manage_messages_request()) {
- RepeatedPtrField<PublishedMessage>* messages = request
- ->mutable_manage_messages_request()->mutable_message_to_publish();
- for (int i = 0; i < messages->size(); ++i) {
- PublishedMessage* message = messages->Mutable(i);
- if (!message->has_opt_in_state_filter())
- message->set_allocated_opt_in_state_filter(CreateOptedInOrOutFilter());
- }
- }
-
- if (request->has_manage_subscriptions_request()) {
- RepeatedPtrField<Subscription>* subscriptions =
- request->mutable_manage_subscriptions_request()->mutable_subscription();
- for (int i = 0; i < subscriptions->size(); ++i) {
- Subscription* subscription = subscriptions->Mutable(i);
- if (!subscription->has_opt_in_state_filter()) {
- subscription->set_allocated_opt_in_state_filter(
- CreateOptedInOrOutFilter());
- }
- }
- }
-}
-
} // namespace
// Public methods
AddPlayingTokens(request.get());
- AllowOptedOutMessages(request.get());
+ // TODO(ckehoe): Currently the server supports only BROADCAST_AND_SCAN.
+ // Remove this once b/16715253 is fixed.
+ if (request->has_manage_messages_request()) {
+ RepeatedPtrField<PublishedMessage>* messages = request
+ ->mutable_manage_messages_request()->mutable_message_to_publish();
+ for (int i = 0; i < messages->size(); ++i) {
+ messages->Mutable(i)->mutable_token_exchange_strategy()
+ ->set_broadcast_scan_configuration(BROADCAST_AND_SCAN);
+ }
+ }
+ if (request->has_manage_subscriptions_request()) {
+ RepeatedPtrField<Subscription>* subscriptions =
+ request->mutable_manage_subscriptions_request()->mutable_subscription();
+ for (int i = 0; i < subscriptions->size(); ++i) {
+ subscriptions->Mutable(i)->mutable_token_exchange_strategy()
+ ->set_broadcast_scan_configuration(BROADCAST_AND_SCAN);
+ }
+ }
+
SendServerRequest(kReportRequestRpcName,
app_id,
request.Pass(),
}
#endif
-TEST_F(RpcHandlerTest, AllowOptedOutMessages) {
- // Request with no filter specified.
- scoped_ptr<ReportRequest> report(new ReportRequest);
- report->mutable_manage_messages_request()->add_message_to_publish()
- ->set_id("message");
- report->mutable_manage_subscriptions_request()->add_subscription()
- ->set_id("subscription");
- rpc_handler_.SendReportRequest(report.Pass());
- const OptInStateFilter& filter =
- GetMessagesPublished().Get(0).opt_in_state_filter();
- ASSERT_EQ(2, filter.allowed_opt_in_state_size());
- EXPECT_EQ(OPTED_IN, filter.allowed_opt_in_state(0));
- EXPECT_EQ(OPTED_OUT, filter.allowed_opt_in_state(1));
- EXPECT_EQ(2, GetSubscriptionsSent().Get(0).opt_in_state_filter()
- .allowed_opt_in_state_size());
-
- // Request with filters already specified.
- report.reset(new ReportRequest);
- report->mutable_manage_messages_request()->add_message_to_publish()
- ->mutable_opt_in_state_filter()->add_allowed_opt_in_state(OPTED_IN);
- report->mutable_manage_subscriptions_request()->add_subscription()
- ->mutable_opt_in_state_filter()->add_allowed_opt_in_state(OPTED_OUT);
- rpc_handler_.SendReportRequest(report.Pass());
- const OptInStateFilter& publish_filter =
- GetMessagesPublished().Get(0).opt_in_state_filter();
- ASSERT_EQ(1, publish_filter.allowed_opt_in_state_size());
- EXPECT_EQ(OPTED_IN, publish_filter.allowed_opt_in_state(0));
- const OptInStateFilter& subscription_filter =
- GetSubscriptionsSent().Get(0).opt_in_state_filter();
- ASSERT_EQ(1, subscription_filter.allowed_opt_in_state_size());
- EXPECT_EQ(OPTED_OUT, subscription_filter.allowed_opt_in_state(0));
-}
-
TEST_F(RpcHandlerTest, CreateRequestHeader) {
SetDeviceId("CreateRequestHeader Device ID");
rpc_handler_.SendReportRequest(make_scoped_ptr(new ReportRequest),
#include "base/time/time.h"
#include "components/data_reduction_proxy/common/data_reduction_proxy_switches.h"
#include "net/base/host_port_pair.h"
+#include "net/proxy/proxy_config.h"
#include "net/proxy/proxy_info.h"
#include "net/proxy/proxy_retry_info.h"
#include "net/proxy/proxy_server.h"
return IsDataReductionProxy(result.proxy_server().host_port_pair(), NULL);
}
+bool DataReductionProxyParams::IsBypassedByDataReductionProxyLocalRules(
+ const net::URLRequest& request,
+ const net::ProxyConfig& data_reduction_proxy_config) const {
+ DCHECK(request.context());
+ DCHECK(request.context()->proxy_service());
+ net::ProxyInfo result;
+ data_reduction_proxy_config.proxy_rules().Apply(
+ request.url(), &result);
+ if (!result.proxy_server().is_valid())
+ return true;
+ if (result.proxy_server().is_direct())
+ return true;
+ return !IsDataReductionProxy(result.proxy_server().host_port_pair(), NULL);
+}
+
std::string DataReductionProxyParams::GetDefaultDevOrigin() const {
#if defined(DATA_REDUCTION_DEV_HOST)
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
if (retry_map.size() == 0)
return false;
- if (is_https && alt_allowed_) {
- return ArePrimaryAndFallbackBypassed(
- retry_map, ssl_origin_, GURL(), min_retry_delay);
+ // If the request is https, consider only the ssl proxy.
+ if (is_https) {
+ if (alt_allowed_) {
+ return ArePrimaryAndFallbackBypassed(
+ retry_map, ssl_origin_, GURL(), min_retry_delay);
+ }
+ NOTREACHED();
+ return false;
}
if (allowed_ && ArePrimaryAndFallbackBypassed(
const GURL& primary,
const GURL& fallback,
base::TimeDelta* min_retry_delay) const {
- net::ProxyRetryInfoMap::const_iterator found = retry_map.find(
- net::ProxyServer(primary.SchemeIs(url::kHttpsScheme) ?
- net::ProxyServer::SCHEME_HTTPS : net::ProxyServer::SCHEME_HTTP,
- net::HostPortPair::FromURL(primary)).ToURI());
-
- if (found == retry_map.end())
- return false;
-
- base::TimeDelta min_delay = found->second.current_delay;
- if (!fallback_allowed_ || !fallback.is_valid()) {
- if (min_retry_delay != NULL)
- *min_retry_delay = min_delay;
- return true;
+ net::ProxyRetryInfoMap::const_iterator found = retry_map.end();
+ if (min_retry_delay)
+ *min_retry_delay = base::TimeDelta::Max();
+
+ // Look for the primary proxy in the retry map. This must be done before
+ // looking for the fallback in order to assign |min_retry_delay| if the
+ // primary proxy has a shorter delay.
+ if (!fallback_allowed_ || !fallback.is_valid() || min_retry_delay) {
+ found = retry_map.find(
+ net::ProxyServer(primary.SchemeIs(url::kHttpsScheme) ?
+ net::ProxyServer::SCHEME_HTTPS :
+ net::ProxyServer::SCHEME_HTTP,
+ net::HostPortPair::FromURL(primary)).ToURI());
+ if (found != retry_map.end() && min_retry_delay) {
+ *min_retry_delay = found->second.current_delay;
+ }
}
- found = retry_map.find(
- net::ProxyServer(fallback.SchemeIs(url::kHttpsScheme) ?
- net::ProxyServer::SCHEME_HTTPS : net::ProxyServer::SCHEME_HTTP,
- net::HostPortPair::FromURL(fallback)).ToURI());
-
- if (found == retry_map.end())
- return false;
+ if (fallback_allowed_ && fallback.is_valid()) {
+ // If fallback is allowed, only the fallback proxy needs to be on the retry
+ // map to know if there was a bypass. We can reset found and forget if the
+ // primary was on the retry map.
+ found = retry_map.find(
+ net::ProxyServer(fallback.SchemeIs(url::kHttpsScheme) ?
+ net::ProxyServer::SCHEME_HTTPS :
+ net::ProxyServer::SCHEME_HTTP,
+ net::HostPortPair::FromURL(fallback)).ToURI());
+ if (found != retry_map.end() &&
+ min_retry_delay &&
+ *min_retry_delay > found->second.current_delay) {
+ *min_retry_delay = found->second.current_delay;
+ }
+ }
- if (min_delay > found->second.current_delay)
- min_delay = found->second.current_delay;
- if (min_retry_delay != NULL)
- *min_retry_delay = min_delay;
- return true;
+ return found != retry_map.end();
}
std::string DataReductionProxyParams::GetDefaultOrigin() const {
}
namespace net {
+class ProxyConfig;
class URLRequest;
}
// list.
virtual bool IsDataReductionProxyEligible(const net::URLRequest* request);
+ // Returns true if this request would be bypassed by the data request proxy
+ // based on applying the |data_reduction_proxy_config| param rules to the
+ // request URL.
+ bool IsBypassedByDataReductionProxyLocalRules(
+ const net::URLRequest& request,
+ const net::ProxyConfig& data_reduction_proxy_config) const;
+
// Checks if all configured data reduction proxies are in the retry map.
// Returns true if the request is bypassed by all configured data reduction
// proxies and returns the bypass delay in delay_seconds (if not NULL). If
{ // proxy flags
false,
false,
- false,
+ true,
// is https request
true,
// proxies in retry map
false,
true,
// expected result
- false,
+ true,
},
{ // proxy flags
- false,
- false,
+ true,
+ true,
true,
// is https request
true,
},
{ // proxy flags
true,
- true,
- true,
+ false,
+ false,
// is https request
- true,
+ false,
// proxies in retry map
+ true,
false,
false,
false,
false,
- true,
// expected result
true,
},
{ // proxy flags
true,
- false,
+ true,
false,
// is https request
false,
// proxies in retry map
+ false,
+ true,
+ false,
+ false,
+ false,
+ // expected result
+ true,
+ },
+ { // proxy flags
+ false,
+ true,
true,
+ // is https request
+ false,
+ // proxies in retry map
false,
false,
false,
+ true,
false,
// expected result
true,
const net::URLFetcher* source) {
DCHECK(thread_checker_.CalledOnValidThread());
- // The purpose of sending a request for the warmup URL is to warm the
- // connection to the data_reduction_proxy. The result is ignored.
- if (source == warmup_fetcher_.get())
- return;
-
DCHECK(source == fetcher_.get());
net::URLRequestStatus status = source->GetStatus();
if (status.status() == net::URLRequestStatus::FAILED) {
if (DisableIfVPN())
return;
ProbeWhetherDataReductionProxyIsAvailable();
- WarmProxyConnection();
}
}
// Check if the proxy has been restricted explicitly by the carrier.
if (enabled_by_user_ && !disabled_on_vpn_) {
ProbeWhetherDataReductionProxyIsAvailable();
- WarmProxyConnection();
}
}
fetcher_->Start();
}
-net::URLFetcher* DataReductionProxySettings::GetURLFetcherForWarmup() {
- return GetBaseURLFetcher(params_->warmup_url(), net::LOAD_DISABLE_CACHE);
-}
-
-void DataReductionProxySettings::WarmProxyConnection() {
- net::URLFetcher* fetcher = GetURLFetcherForWarmup();
- if (!fetcher)
- return;
- warmup_fetcher_.reset(fetcher);
- warmup_fetcher_->Start();
-}
-
bool DataReductionProxySettings::DisableIfVPN() {
net::NetworkInterfaceList network_interfaces;
GetNetworkList(&network_interfaces, 0);
// Virtual for testing.
virtual net::URLFetcher* GetURLFetcherForAvailabilityCheck();
- // Returns a fetcher to warm up the connection to the data reduction proxy.
- // Virtual for testing.
- virtual net::URLFetcher* GetURLFetcherForWarmup();
-
// Virtualized for unit test support.
virtual PrefService* GetOriginalProfilePrefs();
virtual PrefService* GetLocalStatePrefs();
// failure.
void ProbeWhetherDataReductionProxyIsAvailable();
- // Warms the connection to the data reduction proxy.
- void WarmProxyConnection();
-
// Disables use of the data reduction proxy on VPNs. Returns true if the
// data reduction proxy has been disabled.
bool DisableIfVPN();
bool unreachable_;
scoped_ptr<net::URLFetcher> fetcher_;
- scoped_ptr<net::URLFetcher> warmup_fetcher_;
BooleanPrefMember spdy_proxy_auth_enabled_;
BooleanPrefMember data_reduction_proxy_alternative_enabled_;
namespace {
const char kProbeURLWithOKResponse[] = "http://ok.org/";
-const char kWarmupURLWithNoContentResponse[] = "http://warm.org/";
const char kProxy[] = "proxy";
.Times(AnyNumber())
.WillRepeatedly(Return(&pref_service_));
EXPECT_CALL(*settings, GetURLFetcherForAvailabilityCheck()).Times(0);
- EXPECT_CALL(*settings, GetURLFetcherForWarmup()).Times(0);
EXPECT_CALL(*settings, LogProxyState(_, _, _)).Times(0);
settings_.reset(settings);
configurator_.reset(new TestDataReductionProxyConfig());
template <class C>
void DataReductionProxySettingsTestBase::SetProbeResult(
const std::string& test_url,
- const std::string& warmup_test_url,
const std::string& response,
ProbeURLFetchResult result,
bool success,
static_cast<MockDataReductionProxySettings<C>*>(settings_.get());
if (0 == expected_calls) {
EXPECT_CALL(*settings, GetURLFetcherForAvailabilityCheck()).Times(0);
- EXPECT_CALL(*settings, GetURLFetcherForWarmup()).Times(0);
EXPECT_CALL(*settings, RecordProbeURLFetchResult(_)).Times(0);
} else {
EXPECT_CALL(*settings, RecordProbeURLFetchResult(result)).Times(1);
success ? net::HTTP_OK : net::HTTP_INTERNAL_SERVER_ERROR,
success ? net::URLRequestStatus::SUCCESS :
net::URLRequestStatus::FAILED)));
- EXPECT_CALL(*settings, GetURLFetcherForWarmup())
- .Times(expected_calls)
- .WillRepeatedly(Return(new net::FakeURLFetcher(
- GURL(warmup_test_url),
- settings,
- "",
- success ? net::HTTP_NO_CONTENT : net::HTTP_INTERNAL_SERVER_ERROR,
- success ? net::URLRequestStatus::SUCCESS :
- net::URLRequestStatus::FAILED)));
}
}
template void
DataReductionProxySettingsTestBase::SetProbeResult<DataReductionProxySettings>(
const std::string& test_url,
- const std::string& warmup_test_url,
const std::string& response,
ProbeURLFetchResult result,
bool success,
void DataReductionProxySettingsTestBase::CheckProbe(
bool initially_enabled,
const std::string& probe_url,
- const std::string& warmup_url,
const std::string& response,
bool request_succeeded,
bool expected_enabled,
settings_->enabled_by_user_ = true;
settings_->restricted_by_carrier_ = false;
SetProbeResult(probe_url,
- warmup_url,
response,
FetchResult(initially_enabled,
request_succeeded && (response == "OK")),
void DataReductionProxySettingsTestBase::CheckProbeOnIPChange(
const std::string& probe_url,
- const std::string& warmup_url,
const std::string& response,
bool request_succeeded,
bool expected_restricted,
bool expected_fallback_restricted) {
SetProbeResult(probe_url,
- warmup_url,
response,
FetchResult(!settings_->restricted_by_carrier_,
request_succeeded && (response == "OK")),
bool managed) {
// Always have a sucessful probe for pref change tests.
SetProbeResult(kProbeURLWithOKResponse,
- kWarmupURLWithNoContentResponse,
"OK",
FetchResult(enabled, true),
true,
bool enabled_at_startup) {
base::MessageLoopForUI loop;
SetProbeResult(kProbeURLWithOKResponse,
- kWarmupURLWithNoContentResponse,
"OK",
FetchResult(enabled_at_startup, true),
true,
TestDataReductionProxyParams::HAS_EVERYTHING &
~TestDataReductionProxyParams::HAS_DEV_ORIGIN)) {}
MOCK_METHOD0(GetURLFetcherForAvailabilityCheck, net::URLFetcher*());
- MOCK_METHOD0(GetURLFetcherForWarmup, net::URLFetcher*());
MOCK_METHOD0(GetOriginalProfilePrefs, PrefService*());
MOCK_METHOD0(GetLocalStatePrefs, PrefService*());
MOCK_METHOD3(LogProxyState, void(
template <class C> void SetProbeResult(
const std::string& test_url,
- const std::string& warmup_test_url,
const std::string& response,
ProbeURLFetchResult state,
bool success,
int expected_calls);
virtual void SetProbeResult(const std::string& test_url,
- const std::string& warmup_test_url,
const std::string& response,
ProbeURLFetchResult result,
bool success,
bool expected_fallback_restricted);
void CheckProbe(bool initially_enabled,
const std::string& probe_url,
- const std::string& warmup_url,
const std::string& response,
bool request_success,
bool expected_enabled,
bool expected_restricted,
bool expected_fallback_restricted);
void CheckProbeOnIPChange(const std::string& probe_url,
- const std::string& warmup_url,
const std::string& response,
bool request_success,
bool expected_enabled,
}
virtual void SetProbeResult(const std::string& test_url,
- const std::string& warmup_test_url,
const std::string& response,
ProbeURLFetchResult result,
bool success,
int expected_calls) OVERRIDE {
return DataReductionProxySettingsTestBase::SetProbeResult<C>(
test_url,
- warmup_test_url,
response,
result,
success,
const char kProbeURLWithOKResponse[] = "http://ok.org/";
const char kProbeURLWithBadResponse[] = "http://bad.org/";
const char kProbeURLWithNoResponse[] = "http://no.org/";
-const char kWarmupURLWithNoContentResponse[] = "http://warm.org/";
} // namespace
// Request succeeded but with bad response, expect proxy to be restricted.
CheckProbe(true,
kProbeURLWithBadResponse,
- kWarmupURLWithNoContentResponse,
"Bad",
true,
true,
// Request succeeded with valid response, expect proxy to be unrestricted.
CheckProbe(true,
kProbeURLWithOKResponse,
- kWarmupURLWithNoContentResponse,
"OK",
true,
true,
// Request failed, expect proxy to be enabled but restricted.
CheckProbe(true,
kProbeURLWithNoResponse,
- kWarmupURLWithNoContentResponse,
"",
false,
true,
// state.
CheckProbe(false,
kProbeURLWithOKResponse,
- kWarmupURLWithNoContentResponse,
"OK",
true,
false,
// IP address change triggers a probe that succeeds. Proxy remains
// unrestricted.
CheckProbeOnIPChange(kProbeURLWithOKResponse,
- kWarmupURLWithNoContentResponse,
"OK",
true,
false,
false);
// IP address change triggers a probe that fails. Proxy is restricted.
CheckProbeOnIPChange(kProbeURLWithBadResponse,
- kWarmupURLWithNoContentResponse,
"Bad",
true,
true,
false);
// IP address change triggers a probe that fails. Proxy remains restricted.
CheckProbeOnIPChange(kProbeURLWithBadResponse,
- kWarmupURLWithNoContentResponse,
"Bad",
true,
true,
false);
// IP address change triggers a probe that succeeds. Proxy is unrestricted.
CheckProbeOnIPChange(kProbeURLWithOKResponse,
- kWarmupURLWithNoContentResponse,
"OK",
true,
false,
0 /* network prefix */
));
CheckProbeOnIPChange(kProbeURLWithOKResponse,
- kWarmupURLWithNoContentResponse,
"OK",
true,
false,
void DataReductionProxyUsageStats::RecordBypassedBytesHistograms(
net::URLRequest& request,
- const BooleanPrefMember& data_reduction_proxy_enabled) {
+ const BooleanPrefMember& data_reduction_proxy_enabled,
+ const net::ProxyConfig& data_reduction_proxy_config) {
int64 content_length = request.received_response_content_length();
+
+ if (data_reduction_proxy_enabled.GetValue()) {
+ LOG(WARNING) << "managed pac: " << (!data_reduction_proxy_config.Equals(
+ request.context()->proxy_service()->config()) ? "true" : "false");
+ }
+
+ if (data_reduction_proxy_enabled.GetValue() &&
+ !data_reduction_proxy_config.Equals(
+ request.context()->proxy_service()->config())) {
+ RecordBypassedBytes(last_bypass_type_,
+ DataReductionProxyUsageStats::MANAGED_PROXY_CONFIG,
+ content_length);
+ return;
+ }
+
if (data_reduction_proxy_params_->WasDataReductionProxyUsed(&request, NULL)) {
RecordBypassedBytes(last_bypass_type_,
DataReductionProxyUsageStats::NOT_BYPASSED,
}
if (data_reduction_proxy_enabled.GetValue() &&
- !data_reduction_proxy_params_->IsDataReductionProxyEligible(&request)) {
+ data_reduction_proxy_params_->IsBypassedByDataReductionProxyLocalRules(
+ request, data_reduction_proxy_config)) {
RecordBypassedBytes(last_bypass_type_,
DataReductionProxyUsageStats::LOCAL_BYPASS_RULES,
content_length);
}
if (triggering_request_) {
- RecordBypassedBytes(last_bypass_type_,
- DataReductionProxyUsageStats::TRIGGERING_REQUEST,
- content_length);
- triggering_request_ = false;
-
// We only record when audio or video triggers a bypass. We don't care
// about audio and video bypassed as collateral damage.
std::string mime_type;
RecordBypassedBytes(last_bypass_type_,
DataReductionProxyUsageStats::AUDIO_VIDEO,
content_length);
+ return;
}
+
+ RecordBypassedBytes(last_bypass_type_,
+ DataReductionProxyUsageStats::TRIGGERING_REQUEST,
+ content_length);
+ triggering_request_ = false;
+ return;
}
if (last_bypass_type_ != BYPASS_EVENT_TYPE_MAX) {
void DataReductionProxyUsageStats::RecordBypassEventHistograms(
const net::ProxyServer& bypassed_proxy,
- int net_error,
- bool did_fallback) const {
+ int net_error) const {
DataReductionProxyTypeInfo data_reduction_proxy_info;
if (bypassed_proxy.is_valid() && !bypassed_proxy.is_direct() &&
data_reduction_proxy_params_->IsDataReductionProxy(
"DataReductionProxy.BypassedBytes.LocalBypassRules",
content_length);
break;
+ case DataReductionProxyUsageStats::MANAGED_PROXY_CONFIG:
+ UMA_HISTOGRAM_COUNTS(
+ "DataReductionProxy.BypassedBytes.ManagedProxyConfig",
+ content_length);
+ break;
case DataReductionProxyUsageStats::AUDIO_VIDEO:
if (last_bypass_type_ == BYPASS_EVENT_TYPE_SHORT) {
UMA_HISTOGRAM_COUNTS(
// cause the current bypass.
void SetBypassType(DataReductionProxyBypassType type);
- // Given the |content_length| and associated |request|, records the
- // number of bypassed bytes for that |request| into UMAs based on bypass type.
- // |data_reduction_proxy_enabled| tells us the state of the
- // kDataReductionProxyEnabled preference.
+ // Given |data_reduction_proxy_enabled|, a |request|, and the
+ // |data_reduction_proxy_config| records the number of bypassed bytes for that
+ // |request| into UMAs based on bypass type. |data_reduction_proxy_enabled|
+ // tells us the state of the kDataReductionProxyEnabled preference.
void RecordBypassedBytesHistograms(
net::URLRequest& request,
- const BooleanPrefMember& data_reduction_proxy_enabled);
+ const BooleanPrefMember& data_reduction_proxy_enabled,
+ const net::ProxyConfig& data_reduction_proxy_config);
void RecordBypassEventHistograms(const net::ProxyServer& bypassed_proxy,
- int net_error,
- bool did_fallback) const;
+ int net_error) const;
private:
enum BypassedBytesType {
NOT_BYPASSED = 0, /* Not bypassed. */
SSL, /* Bypass due to SSL. */
LOCAL_BYPASS_RULES, /* Bypass due to client-side bypass rules. */
+ MANAGED_PROXY_CONFIG, /* Bypass due to managed config. */
AUDIO_VIDEO, /* Audio/Video bypass. */
TRIGGERING_REQUEST, /* Triggering request bypass. */
NETWORK_ERROR, /* Network error. */
'domain_reliability/baked_in_configs/redirector_gvt1_com.json',
'domain_reliability/baked_in_configs/s0_2mdn_net.json',
'domain_reliability/baked_in_configs/ssl_gstatic_com.json',
+ 'domain_reliability/baked_in_configs/star_admob_com.json',
+ 'domain_reliability/baked_in_configs/star_doubleclick_net.json',
+ 'domain_reliability/baked_in_configs/star_g_doubleclick_net.json',
+ 'domain_reliability/baked_in_configs/star_ggpht_com.json',
+ 'domain_reliability/baked_in_configs/star_google_cn.json',
+ 'domain_reliability/baked_in_configs/star_google_co_uk.json',
+ 'domain_reliability/baked_in_configs/star_google_com.json',
+ 'domain_reliability/baked_in_configs/star_google_com_au.json',
+ 'domain_reliability/baked_in_configs/star_google_de.json',
+ 'domain_reliability/baked_in_configs/star_google_fr.json',
+ 'domain_reliability/baked_in_configs/star_google_it.json',
+ 'domain_reliability/baked_in_configs/star_google_jp.json',
+ 'domain_reliability/baked_in_configs/star_google_org.json',
+ 'domain_reliability/baked_in_configs/star_google_ru.json',
+ 'domain_reliability/baked_in_configs/star_googleadservices_com.json',
+ 'domain_reliability/baked_in_configs/star_googleapis_com.json',
+ 'domain_reliability/baked_in_configs/star_googlesyndication_com.json',
+ 'domain_reliability/baked_in_configs/star_googleusercontent_com.json',
+ 'domain_reliability/baked_in_configs/star_googlevideo_com.json',
+ 'domain_reliability/baked_in_configs/star_gstatic_com.json',
+ 'domain_reliability/baked_in_configs/star_gvt1_com.json',
+ 'domain_reliability/baked_in_configs/star_youtube_com.json',
+ 'domain_reliability/baked_in_configs/star_ytimg_com.json',
'domain_reliability/baked_in_configs/t0_gstatic_com.json',
'domain_reliability/baked_in_configs/t1_gstatic_com.json',
'domain_reliability/baked_in_configs/t2_gstatic_com.json',
# Chrome, to ensure incorrect ones are not added accidentally. Subdomains of
# whitelist entries are also allowed (e.g. maps.google.com, ssl.gstatic.com).
DOMAIN_WHITELIST = ('2mdn.net', 'admob.com', 'doubleclick.net', 'ggpht.com',
- 'google.com', 'googleadservices.com', 'googleapis.com',
- 'googlesyndication.com', 'googleusercontent.com',
- 'googlevideo.com', 'gstatic.com', 'gvt1.com', 'youtube.com')
+ 'google.cn', 'google.co.uk', 'google.com', 'google.com.au',
+ 'google.de', 'google.fr', 'google.it', 'google.jp',
+ 'google.org', 'google.ru', 'googleadservices.com',
+ 'googleapis.com', 'googlesyndication.com',
+ 'googleusercontent.com', 'googlevideo.com', 'gstatic.com',
+ 'gvt1.com', 'youtube.com', 'ytimg.com')
CC_HEADER = """// Copyright (C) 2014 The Chromium Authors. All rights reserved.
{
"config_version": "accounts-google-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "accounts.google.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "ad-doubleclick-net-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "ad.doubleclick.net",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "apis-google-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "apis.google.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "c-admob-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "c.admob.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
--- /dev/null
+{
+ "config_version": "clients2-google-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "clients2.google.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "clients2-google-com-domainreliability",
+ "url_patterns": ["http*://clients2.google.com/domainreliability/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 0.50
+ },
+ {
+ "resource_name": "clients2-google-com-other",
+ "url_patterns": ["http*://clients2.google.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
{
"config_version": "csi-gstatic-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "csi.gstatic.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "ddm-google-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "ddm.google.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "docs-google-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "docs.google.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "drive-google-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "drive.google.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "e-admob-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "e.admob.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "fonts-googleapis-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "fonts.googleapis.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "googleads4-g-doubleclick-net-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "googleads4.g.doubleclick.net",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "googleads-g-doubleclick-net-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "googleads.g.doubleclick.net",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "gstatic-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "gstatic.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "lh3-ggpht-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "lh3.ggpht.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "lh4-ggpht-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "lh4.ggpht.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "lh5-ggpht-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "lh5.ggpht.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "lh6-ggpht-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "lh6.ggpht.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "mail-google-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "mail.google.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "media-admob-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "media.admob.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "pagead2-googlesyndication-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "pagead2.googlesyndication.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "partner-googleadservices-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "partner.googleadservices.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "pubads-g-doubleclick-net-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "pubads.g.doubleclick.net",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "redirector-googlevideo-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "redirector.googlevideo.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "s0-2mdn-net-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "s0.2mdn.net",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "ssl-gstatic-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "ssl.gstatic.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
--- /dev/null
+{
+ "config_version": "star-admob-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.admob.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-admob-com-other",
+ "url_patterns": ["http*://*.admob.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-doubleclick-net-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.doubleclick.net",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-doubleclick-net-other",
+ "url_patterns": ["http*://*.doubleclick.net/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-g-doubleclick-net-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.g.doubleclick.net",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-g-doubleclick-net-other",
+ "url_patterns": ["http*://*.g.doubleclick.net/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-ggpht-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.ggpht.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-ggpht-com-other",
+ "url_patterns": ["http*://*.ggpht.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-google-cn-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.google.cn",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-google-cn-other",
+ "url_patterns": ["http*://*.google.cn/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-google-co-uk-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.google.co.uk",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-google-co-uk-other",
+ "url_patterns": ["http*://*.google.co.uk/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-google-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.google.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-google-com-other",
+ "url_patterns": ["http*://*.google.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-google-com-au-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.google.com.au",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-google-com-au-other",
+ "url_patterns": ["http*://*.google.com.au/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-google-de-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.google.de",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-google-de-other",
+ "url_patterns": ["http*://*.google.de/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-google-fr-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.google.fr",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-google-fr-other",
+ "url_patterns": ["http*://*.google.fr/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-google-it-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.google.it",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-google-it-other",
+ "url_patterns": ["http*://*.google.it/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-google-jp-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.google.jp",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-google-jp-other",
+ "url_patterns": ["http*://*.google.jp/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-google-org-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.google.org",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-google-org-other",
+ "url_patterns": ["http*://*.google.org/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-google-ru-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.google.ru",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-google-ru-other",
+ "url_patterns": ["http*://*.google.ru/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-googleadservices-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.googleadservices.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-googleadservices-com-other",
+ "url_patterns": ["http*://*.googleadservices.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-googleapis-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.googleapis.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-googleapis-com-other",
+ "url_patterns": ["http*://*.googleapis.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-googlesyndication-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.googlesyndication.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-googlesyndication-com-other",
+ "url_patterns": ["http*://*.googlesyndication.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-googleusercontent-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.googleusercontent.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-googleusercontent-com-other",
+ "url_patterns": ["http*://*.googleusercontent.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-googlevideo-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.googlevideo.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-googlevideo-com-redirector",
+ "url_patterns": ["http*://redirector.googlevideo.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ },
+ {
+ "resource_name": "star-googlevideo-com-rname",
+ "url_patterns": ["http*://r*.googlevideo.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ },
+ {
+ "resource_name": "star-googlevideo-com-other",
+ "url_patterns": ["http*://*.googlevideo.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-gstatic-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.gstatic.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-gstatic-com-other",
+ "url_patterns": ["http*://*.gstatic.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-gvt1-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.gvt1.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-gvt1-com-domain-reliability",
+ "url_patterns": ["http*://domain-reliability.gvt1.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 0.50
+ },
+ {
+ "resource_name": "star-gvt1-com-redirector",
+ "url_patterns": ["http*://redirector.gvt1.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ },
+ {
+ "resource_name": "star-gvt1-com-rname",
+ "url_patterns": ["http*://r*.gvt1.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ },
+ {
+ "resource_name": "star-gvt1-com-other",
+ "url_patterns": ["http*://*.gvt1.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-youtube-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.youtube.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-youtube-com-other",
+ "url_patterns": ["http*://*.youtube.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
--- /dev/null
+{
+ "config_version": "star-ytimg-com-v1",
+ "config_valid_until": 1425168000.0,
+ "monitored_domain": "*.ytimg.com",
+ "collectors": [
+ {
+ "upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
+ }
+ ],
+ "monitored_resources": [
+ {
+ "resource_name": "star-ytimg-com-other",
+ "url_patterns": ["http*://*.ytimg.com/*"],
+ "success_sample_rate": 0.05,
+ "failure_sample_rate": 1.00
+ }
+ ]
+}
{
"config_version": "t0-gstatic-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "t0.gstatic.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "t1-gstatic-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "t1.gstatic.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "t2-gstatic-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "t2.gstatic.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "t3-gstatic-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "t3.gstatic.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "themes-googleusercontent-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "themes.googleusercontent.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "www-google-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "www.google.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "www-googleadservices-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "www.googleadservices.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "www-gstatic-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "www.gstatic.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
{
"config_version": "www-youtube-com-v1",
- "config_valid_until": 1413331200.0,
+ "config_valid_until": 1425168000.0,
"monitored_domain": "www.youtube.com",
"collectors": [
{
"upload_url": "https://clients2.google.com/domainreliability/upload"
+ },
+ {
+ "upload_url": "https://beacons.gvt2.com/domainreliability/upload"
}
],
"monitored_resources": [
url_request.addHTTPHeaderField(
blink::WebString::fromUTF8("Accept"),
blink::WebString::fromUTF8("application/x-pnacl, */*"));
+ url_request.setRequestContext(blink::WebURLRequest::RequestContextObject);
downloader->Load(url_request);
}
return;
}
+ // Upload credentials the first time they are saved. This data is used
+ // by password generation to help determine account creation sites.
+ // Blacklisted credentials will never be used, so don't upload a vote for
+ // them.
+ if (!pending_credentials_.blacklisted_by_user)
+ UploadPasswordForm(pending_credentials_.form_data, autofill::PASSWORD);
+
pending_credentials_.date_created = Time::Now();
SanitizePossibleUsernames(&pending_credentials_);
password_store->AddLogin(pending_credentials_);
if (!pending.form_data.fields.empty() &&
pending_structure.FormSignature() !=
observed_structure.FormSignature()) {
- autofill::AutofillManager* autofill_manager =
- driver_->GetAutofillManager();
- if (autofill_manager) {
- // Note that this doesn't guarantee that the upload succeeded, only that
- // |pending.form_data| is considered uploadable.
- bool success =
- autofill_manager->UploadPasswordGenerationForm(pending.form_data);
- UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", success);
- }
+ UploadPasswordForm(pending.form_data,
+ autofill::ACCOUNT_CREATION_PASSWORD);
}
}
}
+void PasswordFormManager::UploadPasswordForm(
+ const autofill::FormData& form_data,
+ const autofill::ServerFieldType& password_type) {
+ autofill::AutofillManager* autofill_manager =
+ driver_->GetAutofillManager();
+ if (!autofill_manager)
+ return;
+
+ // Note that this doesn't guarantee that the upload succeeded, only that
+ // |form_data| is considered uploadable.
+ bool success =
+ autofill_manager->UploadPasswordForm(
+ form_data, autofill::ACCOUNT_CREATION_PASSWORD);
+ UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", success);
+}
+
int PasswordFormManager::ScoreResult(const PasswordForm& candidate) const {
DCHECK_EQ(state_, MATCHING_PHASE);
// For scoring of candidate login data:
#include "build/build_config.h"
#include "base/stl_util.h"
+#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/password_manager_driver.h"
#include "components/password_manager/core/browser/password_store.h"
// duplicates.
void SanitizePossibleUsernames(autofill::PasswordForm* form);
+ // Helper function to delegate uploading to the AutofillManager.
+ virtual void UploadPasswordForm(
+ const autofill::FormData& form_data,
+ const autofill::ServerFieldType& password_type);
+
// Set of PasswordForms from the DB that best match the form
// being managed by this. Use a map instead of vector, because we most
// frequently require lookups by username value in IsNewLogin.
TestingPrefServiceSimple prefs_;
PasswordStore* password_store_;
- MockPasswordManagerDriver driver_;
+ testing::NiceMock<MockPasswordManagerDriver> driver_;
};
class TestPasswordManager : public PasswordManager {
mutable autofill::PasswordFormMap best_matches_;
};
+class MockPasswordFormManager : public PasswordFormManager {
+ public:
+ MockPasswordFormManager(PasswordManager* manager,
+ PasswordManagerClient* client,
+ PasswordManagerDriver* driver,
+ const autofill::PasswordForm& observed_form,
+ bool ssl_valid)
+ : PasswordFormManager(manager, client, driver, observed_form, ssl_valid)
+ {}
+
+ MOCK_METHOD2(UploadPasswordForm, void(const autofill::FormData&,
+ const autofill::ServerFieldType&));
+};
+
} // namespace
class PasswordFormManagerTest : public testing::Test {
}
void InitializeMockStore() {
- if (!mock_store_) {
- mock_store_ = new MockPasswordStore();
- ASSERT_TRUE(mock_store_);
+ if (!mock_store_.get()) {
+ mock_store_ = new testing::NiceMock<MockPasswordStore>();
+ ASSERT_TRUE(mock_store_.get());
}
}
private:
PasswordForm observed_form_;
PasswordForm saved_match_;
- scoped_refptr<MockPasswordStore> mock_store_;
+ scoped_refptr<testing::NiceMock<MockPasswordStore> > mock_store_;
TestPasswordManagerClient client_;
};
password_store->Shutdown();
}
+TEST_F(PasswordFormManagerTest, UploadFormData_NewPassword) {
+ InitializeMockStore();
+ TestPasswordManagerClient client_with_store(mock_store());
+ TestPasswordManager password_manager(&client_with_store);
+ EXPECT_CALL(*client_with_store.GetMockDriver(), IsOffTheRecord())
+ .WillRepeatedly(Return(false));
+
+ PasswordForm form(*observed_form());
+
+ autofill::FormFieldData field;
+ field.label = ASCIIToUTF16("full_name");
+ field.name = ASCIIToUTF16("full_name");
+ field.form_control_type = "text";
+ form.form_data.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Email");
+ field.name = ASCIIToUTF16("Email");
+ field.form_control_type = "text";
+ form.form_data.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("password");
+ field.name = ASCIIToUTF16("password");
+ field.form_control_type = "password";
+ form.form_data.fields.push_back(field);
+
+ // For newly saved passwords, upload a vote for autofill::PASSWORD.
+ MockPasswordFormManager form_manager(&password_manager,
+ &client_with_store,
+ client_with_store.GetDriver(),
+ form,
+ false);
+ SimulateMatchingPhase(&form_manager, RESULT_NO_MATCH);
+
+ PasswordForm form_to_save(form);
+ form_to_save.preferred = true;
+ form_to_save.username_value = ASCIIToUTF16("username");
+ form_to_save.password_value = ASCIIToUTF16("1234");
+
+ EXPECT_CALL(form_manager, UploadPasswordForm(_, autofill::PASSWORD)).Times(1);
+ form_manager.ProvisionallySave(
+ form_to_save,
+ PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
+ form_manager.Save();
+ Mock::VerifyAndClearExpectations(&form_manager);
+
+ // Do not upload a vote if the user is blacklisting the form.
+ MockPasswordFormManager blacklist_form_manager(&password_manager,
+ &client_with_store,
+ client_with_store.GetDriver(),
+ form,
+ false);
+ SimulateMatchingPhase(&blacklist_form_manager, RESULT_NO_MATCH);
+
+ EXPECT_CALL(blacklist_form_manager,
+ UploadPasswordForm(_, autofill::PASSWORD)).Times(0);
+ blacklist_form_manager.PermanentlyBlacklist();
+ Mock::VerifyAndClearExpectations(&blacklist_form_manager);
+}
+
+TEST_F(PasswordFormManagerTest, UploadFormData_AccountCreationPassword) {
+ InitializeMockStore();
+ TestPasswordManagerClient client_with_store(mock_store());
+ TestPasswordManager password_manager(&client_with_store);
+ EXPECT_CALL(*client_with_store.GetMockDriver(), IsOffTheRecord())
+ .WillRepeatedly(Return(false));
+
+ PasswordForm form(*observed_form());
+
+ autofill::FormFieldData field;
+ field.label = ASCIIToUTF16("Email");
+ field.name = ASCIIToUTF16("Email");
+ field.form_control_type = "text";
+ form.form_data.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("password");
+ field.name = ASCIIToUTF16("password");
+ field.form_control_type = "password";
+ form.form_data.fields.push_back(field);
+
+ MockPasswordFormManager form_manager(&password_manager,
+ &client_with_store,
+ client_with_store.GetDriver(),
+ form,
+ false);
+ std::vector<PasswordForm*> result;
+ result.push_back(CreateSavedMatch(false));
+
+ field.label = ASCIIToUTF16("full_name");
+ field.name = ASCIIToUTF16("full_name");
+ field.form_control_type = "text";
+ result[0]->form_data.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Email");
+ field.name = ASCIIToUTF16("Email");
+ field.form_control_type = "text";
+ result[0]->form_data.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("password");
+ field.name = ASCIIToUTF16("password");
+ field.form_control_type = "password";
+ result[0]->form_data.fields.push_back(field);
+
+ PasswordForm form_to_save(form);
+ form_to_save.preferred = true;
+ form_to_save.username_value = result[0]->username_value;
+ form_to_save.password_value = result[0]->password_value;
+
+ SimulateFetchMatchingLoginsFromPasswordStore(&form_manager);
+ SimulateResponseFromPasswordStore(&form_manager, result);
+
+ EXPECT_CALL(form_manager,
+ UploadPasswordForm(_,
+ autofill::ACCOUNT_CREATION_PASSWORD)).Times(1);
+ form_manager.ProvisionallySave(
+ form_to_save,
+ PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
+ form_manager.Save();
+}
+
} // namespace password_manager
If you disable this setting, users will not be allowed to use EasyUnlock.
- If this policy is left not set, EasyUnlock is allowed if the requirements for the feature are satified.
- ''',
+ If this policy is left not set, the default is not allowed for enterprise-managed users and allowed for non-managed users.''',
},
{
'name': 'SessionLocales',
'name': 'BrowserGuestModeEnabled',
'type': 'main',
'schema': { 'type': 'boolean' },
- 'supported_on': ['chrome.*:39-'],
+ 'supported_on': ['chrome.*:38-'],
'features': {
'dynamic_refresh': True,
'per_profile': False,
source_web_contents_(WebContents::FromRenderFrameHost(render_frame_host)),
browser_context_(source_web_contents_->GetBrowserContext()),
menu_model_(this),
+ render_frame_id_(render_frame_host->GetRoutingID()),
command_executed_(false),
- render_process_id_(render_frame_host->GetProcess()->GetID()),
- render_frame_id_(render_frame_host->GetRoutingID()) {
+ render_process_id_(render_frame_host->GetProcess()->GetID()) {
}
RenderViewContextMenuBase::~RenderViewContextMenuBase() {
ui::SimpleMenuModel menu_model_;
+ // Renderer's frame id.
+ int render_frame_id_;
+
// Our observers.
mutable ObserverList<RenderViewContextMenuObserver> observers_;
// The RenderFrameHost's IDs.
int render_process_id_;
- int render_frame_id_;
scoped_ptr<ToolkitDelegate> toolkit_delegate_;
'suggestions/suggestions_service.h',
'suggestions/suggestions_store.cc',
'suggestions/suggestions_store.h',
+ 'suggestions/suggestions_utils.cc',
+ 'suggestions/suggestions_utils.h',
],
'variables': {
'proto_in_dir': 'suggestions/proto',
"suggestions_service.h",
"suggestions_store.cc",
"suggestions_store.h",
+ "suggestions_utils.cc",
+ "suggestions_utils.h",
]
deps = [
}
void SuggestionsService::FetchSuggestionsData(
+ SyncState sync_state,
SuggestionsService::ResponseCallback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
+ if (sync_state == NOT_INITIALIZED_ENABLED) {
+ // Sync is not initialized yet, but enabled. Serve previously cached
+ // suggestions if available.
+ waiting_requestors_.push_back(callback);
+ ServeFromCache();
+ return;
+ } else if (sync_state == SYNC_OR_HISTORY_SYNC_DISABLED) {
+ // Cancel any ongoing request (and the timeout closure). We must no longer
+ // interact with the server.
+ pending_request_.reset(NULL);
+ pending_timeout_closure_.reset(NULL);
+ suggestions_store_->ClearSuggestions();
+ callback.Run(SuggestionsProfile());
+ DispatchRequestsAndClear(SuggestionsProfile(), &waiting_requestors_);
+ return;
+ }
FetchSuggestionsDataNoTimeout(callback);
base::TimeDelta::FromMilliseconds(request_timeout_ms_));
}
-void SuggestionsService::FetchSuggestionsDataNoTimeout(
- SuggestionsService::ResponseCallback callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (pending_request_.get()) {
- // Request already exists, so just add requestor to queue.
- waiting_requestors_.push_back(callback);
- return;
- }
-
- // Form new request.
- DCHECK(waiting_requestors_.empty());
- waiting_requestors_.push_back(callback);
- IssueRequest(suggestions_url_);
-}
-
void SuggestionsService::GetPageThumbnail(
const GURL& url,
base::Callback<void(const GURL&, const SkBitmap*)> callback) {
BlacklistStore::RegisterProfilePrefs(registry);
}
+void SuggestionsService::SetDefaultExpiryTimestamp(
+ SuggestionsProfile* suggestions, int64 default_timestamp_usec) {
+ for (int i = 0; i < suggestions->suggestions_size(); ++i) {
+ ChromeSuggestion* suggestion = suggestions->mutable_suggestions(i);
+ // Do not set expiry if the server has already provided a more specific
+ // expiry time for this suggestion.
+ if (!suggestion->has_expiry_ts()) {
+ suggestion->set_expiry_ts(default_timestamp_usec);
+ }
+ }
+}
+
+void SuggestionsService::FetchSuggestionsDataNoTimeout(
+ SuggestionsService::ResponseCallback callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (pending_request_.get()) {
+ // Request already exists, so just add requestor to queue.
+ waiting_requestors_.push_back(callback);
+ return;
+ }
+
+ // Form new request.
+ DCHECK(waiting_requestors_.empty());
+ waiting_requestors_.push_back(callback);
+ IssueRequest(suggestions_url_);
+}
+
void SuggestionsService::IssueRequest(const GURL& url) {
pending_request_.reset(CreateSuggestionsRequest(url));
pending_request_->Start();
ScheduleBlacklistUpload(true);
}
-void SuggestionsService::SetDefaultExpiryTimestamp(
- SuggestionsProfile* suggestions, int64 default_timestamp_usec) {
- for (int i = 0; i < suggestions->suggestions_size(); ++i) {
- ChromeSuggestion* suggestion = suggestions->mutable_suggestions(i);
- // Do not set expiry if the server has already provided a more specific
- // expiry time for this suggestion.
- if (!suggestion->has_expiry_ts()) {
- suggestion->set_expiry_ts(default_timestamp_usec);
- }
- }
-}
-
void SuggestionsService::Shutdown() {
// Cancel pending request and timeout closure, then serve existing requestors
// from cache.
#include "components/keyed_service/core/keyed_service.h"
#include "components/suggestions/image_manager.h"
#include "components/suggestions/proto/suggestions.pb.h"
+#include "components/suggestions/suggestions_utils.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"
// Whether the user is part of a control group.
static bool IsControlGroup();
- // Request suggestions data, which will be passed to |callback|. Initiates a
- // fetch request unless a pending one exists. To prevent multiple requests,
- // we place all |callback|s in a queue and update them simultaneously when
- // fetch request completes. Also posts a task to execute OnRequestTimeout
- // if the request hasn't completed in a given amount of time.
- void FetchSuggestionsData(ResponseCallback callback);
-
- // Similar to FetchSuggestionsData but doesn't post a task to execute
- // OnDelaySinceFetch.
- void FetchSuggestionsDataNoTimeout(ResponseCallback callback);
+ // Request suggestions data, which will be passed to |callback|. |sync_state|
+ // will influence the behavior of this function (see SyncState definition).
+ //
+ // |sync_state| must be specified based on the current state of the system
+ // (see suggestions::GetSyncState). Callers should call this function again if
+ // sync state changes.
+ //
+ // If state allows for a network request, it is initiated unless a pending one
+ // exists. To prevent multiple requests, all |callback|s are placed in a queue
+ // and are updated simultaneously when the fetch completes. Also posts a task
+ // to execute OnRequestTimeout if the request hasn't completed in a given
+ // amount of time.
+ void FetchSuggestionsData(SyncState sync_state,
+ ResponseCallback callback);
// Retrieves stored thumbnail for website |url| asynchronously. Calls
// |callback| with Bitmap pointer if found, and NULL otherwise.
void SetDefaultExpiryTimestamp(SuggestionsProfile* suggestions,
int64 timestamp_usec);
private:
+ friend class SuggestionsServiceTest;
FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, BlacklistURLFails);
FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, FetchSuggestionsData);
FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, UpdateBlacklistDelay);
+ // Similar to FetchSuggestionsData but doesn't post a task to execute
+ // OnDelaySinceFetch.
+ void FetchSuggestionsDataNoTimeout(ResponseCallback callback);
+
// Issue a request.
void IssueRequest(const GURL& url);
#include "components/suggestions/image_manager.h"
#include "components/suggestions/proto/suggestions.pb.h"
#include "components/suggestions/suggestions_store.h"
+#include "components/suggestions/suggestions_utils.h"
#include "components/variations/entropy_provider.h"
#include "components/variations/variations_associated_data.h"
#include "net/http/http_response_headers.h"
// Send the request. Empty data will be returned to the callback.
suggestions_service->FetchSuggestionsData(
+ INITIALIZED_ENABLED_HISTORY, // Normal mode.
base::Bind(&SuggestionsServiceTest::ExpectEmptySuggestionsProfile,
base::Unretained(this)));
// Send the request. Empty data will be returned to the callback.
suggestions_service->FetchSuggestionsData(
+ INITIALIZED_ENABLED_HISTORY, // Normal mode.
base::Bind(&SuggestionsServiceTest::ExpectEmptySuggestionsProfile,
base::Unretained(this)));
EXPECT_EQ(1, suggestions_empty_data_count_);
}
+TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncDisabled) {
+ // Field trial enabled with a specific suggestions URL.
+ EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams,
+ kFakeBlacklistPath, kFakeBlacklistUrlParam, false);
+ scoped_ptr<SuggestionsService> suggestions_service(
+ CreateSuggestionsServiceWithMocks());
+ EXPECT_TRUE(suggestions_service != NULL);
+
+ // Set up expectations on the SuggestionsStore.
+ EXPECT_CALL(*mock_suggestions_store_, ClearSuggestions());
+
+ // Send the request. Cache is cleared and empty data will be returned to the
+ // callback.
+ suggestions_service->FetchSuggestionsData(
+ SYNC_OR_HISTORY_SYNC_DISABLED,
+ base::Bind(&SuggestionsServiceTest::ExpectEmptySuggestionsProfile,
+ base::Unretained(this)));
+
+ // Wait for posted task to complete.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Ensure that ExpectEmptySuggestionsProfile ran once.
+ EXPECT_EQ(1, suggestions_empty_data_count_);
+}
+
+TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncNotInitializedEnabled) {
+ // Field trial enabled with a specific suggestions URL.
+ EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams,
+ kFakeBlacklistPath, kFakeBlacklistUrlParam, false);
+ scoped_ptr<SuggestionsService> suggestions_service(
+ CreateSuggestionsServiceWithMocks());
+ EXPECT_TRUE(suggestions_service != NULL);
+ scoped_ptr<SuggestionsProfile> suggestions_profile(
+ CreateSuggestionsProfile());
+
+ // Expectations.
+ EXPECT_CALL(*mock_suggestions_store_, LoadSuggestions(_))
+ .WillOnce(DoAll(SetArgPointee<0>(*suggestions_profile), Return(true)));
+ EXPECT_CALL(*mock_thumbnail_manager_,
+ Initialize(EqualsProto(*suggestions_profile)));
+ EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_));
+
+ // Send the request. In this state, cached data will be returned to the
+ // caller.
+ suggestions_service->FetchSuggestionsData(
+ NOT_INITIALIZED_ENABLED,
+ base::Bind(&SuggestionsServiceTest::CheckSuggestionsData,
+ base::Unretained(this)));
+
+ // Wait for posted task to complete.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Ensure that CheckSuggestionsData ran once.
+ EXPECT_EQ(1, suggestions_data_check_count_);
+}
+
TEST_F(SuggestionsServiceTest, BlacklistURL) {
EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams,
kFakeBlacklistPath, kFakeBlacklistUrlParam, false);
--- /dev/null
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/suggestions/suggestions_utils.h"
+
+namespace suggestions {
+
+SyncState GetSyncState(bool sync_enabled,
+ bool sync_initialized,
+ bool history_sync_enabled) {
+ if (!sync_enabled)
+ return SYNC_OR_HISTORY_SYNC_DISABLED;
+
+ if (!sync_initialized)
+ return NOT_INITIALIZED_ENABLED;
+
+ return history_sync_enabled ?
+ INITIALIZED_ENABLED_HISTORY : SYNC_OR_HISTORY_SYNC_DISABLED;
+}
+
+} // namespace suggestions
--- /dev/null
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SUGGESTIONS_SUGGESTIONS_UTILS_H_
+#define COMPONENTS_SUGGESTIONS_SUGGESTIONS_UTILS_H_
+
+namespace suggestions {
+
+// Establishes the different sync states that users of SuggestionsService can
+// specify. There are three different concepts in the sync service: initialized,
+// sync enabled and history sync enabled.
+enum SyncState {
+ // State: Sync service is not initialized, yet not disabled. History sync
+ // state is unknown (since not initialized).
+ // Behavior: Does not issue a server request, but serves from cache if
+ // available.
+ NOT_INITIALIZED_ENABLED,
+
+ // State: Sync service is initialized, sync is enabled and history sync is
+ // enabled.
+ // Behavior: Update suggestions from the server. Serve from cache on timeout.
+ INITIALIZED_ENABLED_HISTORY,
+
+ // State: Sync service is disabled or history sync is disabled.
+ // Behavior: Do not issue a server request. Clear the cache. Serve empty
+ // suggestions.
+ SYNC_OR_HISTORY_SYNC_DISABLED,
+};
+
+// Users of SuggestionsService should always use this function to get SyncState.
+SyncState GetSyncState(bool sync_enabled,
+ bool sync_initialized,
+ bool history_sync_enabled);
+
+} // namespace suggestions
+
+#endif // COMPONENTS_SUGGESTIONS_SUGGESTIONS_UTILS_H_
DCHECK(task_runner_->RunsTasksOnCurrentThread());
User* user = FindUserAndModify(user_id);
- if (!user)
+ if (!user) {
+ LOG(ERROR) << "User not found: " << user_id;
return; // Ignore if there is no such user.
+ }
user->set_display_email(display_email);
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
return;
- Java_ContentViewCore_showPastePopup(env, obj.obj(),
- static_cast<jint>(x_dip),
- static_cast<jint>(y_dip));
+ Java_ContentViewCore_showPastePopupWithFeedback(env, obj.obj(),
+ static_cast<jint>(x_dip),
+ static_cast<jint>(y_dip));
}
void ContentViewCoreImpl::GetScaledContentBitmap(
#include "content/public/browser/android/compositor.h"
#include "content/public/browser/android/content_view_layer_renderer.h"
#include "content/public/browser/android/layer_tree_build_helper.h"
+#include "content/public/browser/android/ui_resource_provider.h"
#include "jni/ContentViewRenderView_jni.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/size.h"
LayerTreeBuildHelper* build_helper =
reinterpret_cast<LayerTreeBuildHelper*>(native_build_helper);
layer_tree_build_helper_.reset(build_helper);
+ InitCompositor();
}
// static
static jlong Init(JNIEnv* env,
void ContentViewRenderView::SetOverlayVideoMode(
JNIEnv* env, jobject obj, bool enabled) {
compositor_->SetHasTransparentBackground(enabled);
+ SetNeedsComposite(env, obj);
+}
+
+void ContentViewRenderView::SetNeedsComposite(JNIEnv* env, jobject obj) {
+ if (compositor_)
+ compositor_->SetNeedsComposite();
}
void ContentViewRenderView::Layout() {
if (!compositor_)
compositor_.reset(Compositor::Create(this, root_window_));
}
+
+jlong ContentViewRenderView::GetUIResourceProvider(JNIEnv* env,
+ jobject obj) {
+ if (!compositor_)
+ return 0;
+ return reinterpret_cast<intptr_t>(&compositor_->GetUIResourceProvider());
+}
} // namespace content
namespace content {
class Compositor;
class LayerTreeBuildHelper;
+class UIResourceProvider;
class ContentViewRenderView : public CompositorClient {
public:
jint format, jint width, jint height, jobject surface);
jboolean Composite(JNIEnv* env, jobject obj);
void SetOverlayVideoMode(JNIEnv* env, jobject obj, bool enabled);
+ void SetNeedsComposite(JNIEnv* env, jobject obj);
+
+ // TODO(yusufo): Remove this once the compositor code is
+ // refactored to use a unified system.
+ jlong GetUIResourceProvider(JNIEnv* env, jobject obj);
// CompositorClient implementation
virtual void Layout() OVERRIDE;
attributes, &in_process_attribs);
in_process_attribs.lose_context_when_out_of_memory = true;
- scoped_ptr<gpu::GLInProcessContext> context(
- gpu::GLInProcessContext::Create(NULL /* service */,
- NULL /* surface */,
- true /* is_offscreen */,
- gfx::kNullAcceleratedWidget,
- gfx::Size(1, 1),
- NULL /* share_context */,
- false /* share_resources */,
- in_process_attribs,
- gpu_preference));
+ scoped_ptr<gpu::GLInProcessContext> context(gpu::GLInProcessContext::Create(
+ NULL /* service */,
+ NULL /* surface */,
+ true /* is_offscreen */,
+ gfx::kNullAcceleratedWidget,
+ gfx::Size(1, 1),
+ NULL /* share_context */,
+ false /* share_resources */,
+ in_process_attribs,
+ gpu_preference,
+ gpu::GLInProcessContextSharedMemoryLimits()));
return context.Pass();
}
scoped_ptr<gpu::GLInProcessContext> CreateContext(
scoped_refptr<gpu::InProcessCommandBuffer::Service> service,
- gpu::GLInProcessContext* share_context) {
+ gpu::GLInProcessContext* share_context,
+ const gpu::GLInProcessContextSharedMemoryLimits& mem_limits) {
const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
gpu::gles2::ContextCreationAttribHelper in_process_attribs;
WebGraphicsContext3DImpl::ConvertAttributes(
GetDefaultAttribs(), &in_process_attribs);
in_process_attribs.lose_context_when_out_of_memory = true;
- scoped_ptr<gpu::GLInProcessContext> context(
- gpu::GLInProcessContext::Create(service,
- NULL /* surface */,
- false /* is_offscreen */,
- gfx::kNullAcceleratedWidget,
- gfx::Size(1, 1),
- share_context,
- false /* share_resources */,
- in_process_attribs,
- gpu_preference));
+ scoped_ptr<gpu::GLInProcessContext> context(gpu::GLInProcessContext::Create(
+ service,
+ NULL /* surface */,
+ false /* is_offscreen */,
+ gfx::kNullAcceleratedWidget,
+ gfx::Size(1, 1),
+ share_context,
+ false /* share_resources */,
+ in_process_attribs,
+ gpu_preference,
+ mem_limits));
return context.Pass();
}
CreateOnscreenContextProviderForCompositorThread() {
DCHECK(service_);
- if (!share_context_.get())
- share_context_ = CreateContext(service_, NULL);
+ if (!share_context_.get()) {
+ share_context_ = CreateContext(
+ service_, NULL, gpu::GLInProcessContextSharedMemoryLimits());
+ }
+ gpu::GLInProcessContextSharedMemoryLimits mem_limits;
+ // This is half of what RenderWidget uses because synchronous compositor
+ // pipeline is only one frame deep.
+ mem_limits.mapped_memory_reclaim_limit = 6 * 1024 * 1024;
return webkit::gpu::ContextProviderInProcess::Create(
- WrapContext(CreateContext(service_, share_context_.get())),
+ WrapContext(CreateContext(service_, share_context_.get(), mem_limits)),
"Child-Compositor");
}
base::AutoLock lock(num_hardware_compositor_lock_);
DCHECK_GT(num_hardware_compositors_, 0u);
num_hardware_compositors_--;
- if (num_hardware_compositors_ == 0) {
- // Nullify the video_context_provider_ now so that it is not null only if
- // there is at least 1 hardware compositor
- video_context_provider_ = NULL;
- }
}
bool SynchronousCompositorFactoryImpl::CanCreateMainThreadContext() {
scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
SynchronousCompositorFactoryImpl::TryCreateStreamTextureFactory() {
- scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
- context_provider;
- // This check only guarantees the main thread context is created after
- // a compositor did successfully initialize hardware draw in the past.
- // When all compositors have released hardware draw, main thread context
- // creation is guaranteed to fail.
- if (CanCreateMainThreadContext() && !video_context_provider_) {
+ // Always fail creation even if |video_context_provider_| is not NULL.
+ // This is to avoid synchronous calls that may deadlock. Setting
+ // |video_context_provider_| to null is also not safe since it makes
+ // synchronous destruction uncontrolled and possibly deadlock.
+ if (!CanCreateMainThreadContext()) {
+ return
+ scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>();
+ }
+
+ if (!video_context_provider_) {
DCHECK(service_);
DCHECK(share_context_.get());
video_context_provider_ = new VideoContextProvider(
- CreateContext(service_, share_context_.get()));
+ CreateContext(service_,
+ share_context_.get(),
+ gpu::GLInProcessContextSharedMemoryLimits()));
}
return video_context_provider_;
}
}
GinJavaBridgeDispatcherHost::~GinJavaBridgeDispatcherHost() {
+ DCHECK(pending_replies_.empty());
}
void GinJavaBridgeDispatcherHost::RenderFrameCreated(
void GinJavaBridgeDispatcherHost::RenderFrameDeleted(
RenderFrameHost* render_frame_host) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ IPC::Message* reply_msg = TakePendingReply(render_frame_host);
+ if (reply_msg != NULL) {
+ base::ListValue result;
+ result.Append(base::Value::CreateNullValue());
+ IPC::WriteParam(reply_msg, result);
+ IPC::WriteParam(reply_msg, kGinJavaBridgeRenderFrameDeleted);
+ render_frame_host->Send(reply_msg);
+ }
RemoveHolder(render_frame_host,
GinJavaBoundObject::ObjectMap::iterator(&objects_),
objects_.size());
return helper->rfh_found();
}
-void GinJavaBridgeDispatcherHost::SendReply(
- RenderFrameHost* render_frame_host,
- IPC::Message* reply_msg) {
- DCHECK_CURRENTLY_ON(BrowserThread::UI);
- if (IsValidRenderFrameHost(render_frame_host)) {
- render_frame_host->Send(reply_msg);
- } else {
- delete reply_msg;
- }
-}
-
void GinJavaBridgeDispatcherHost::OnGetMethods(
RenderFrameHost* render_frame_host,
GinJavaBoundObject::ObjectID object_id,
render_frame_host->Send(reply_msg);
return;
}
+ DCHECK(!HasPendingReply(render_frame_host));
+ pending_replies_[render_frame_host] = reply_msg;
base::PostTaskAndReplyWithResult(
g_background_thread.Get().message_loop()->message_loop_proxy(),
FROM_HERE,
base::Bind(&GinJavaBoundObject::GetMethodNames, object),
base::Bind(&GinJavaBridgeDispatcherHost::SendMethods,
AsWeakPtr(),
- render_frame_host,
- reply_msg));
+ render_frame_host));
}
void GinJavaBridgeDispatcherHost::SendMethods(
RenderFrameHost* render_frame_host,
- IPC::Message* reply_msg,
const std::set<std::string>& method_names) {
+ IPC::Message* reply_msg = TakePendingReply(render_frame_host);
+ if (!reply_msg) {
+ return;
+ }
IPC::WriteParam(reply_msg, method_names);
- SendReply(render_frame_host, reply_msg);
+ render_frame_host->Send(reply_msg);
}
void GinJavaBridgeDispatcherHost::OnHasMethod(
render_frame_host->Send(reply_msg);
return;
}
+ DCHECK(!HasPendingReply(render_frame_host));
+ pending_replies_[render_frame_host] = reply_msg;
base::PostTaskAndReplyWithResult(
g_background_thread.Get().message_loop()->message_loop_proxy(),
FROM_HERE,
base::Bind(&GinJavaBoundObject::HasMethod, object, method_name),
base::Bind(&GinJavaBridgeDispatcherHost::SendHasMethodReply,
AsWeakPtr(),
- render_frame_host,
- reply_msg));
+ render_frame_host));
}
void GinJavaBridgeDispatcherHost::SendHasMethodReply(
RenderFrameHost* render_frame_host,
- IPC::Message* reply_msg,
bool result) {
+ IPC::Message* reply_msg = TakePendingReply(render_frame_host);
+ if (!reply_msg) {
+ return;
+ }
IPC::WriteParam(reply_msg, result);
- SendReply(render_frame_host, reply_msg);
+ render_frame_host->Send(reply_msg);
}
void GinJavaBridgeDispatcherHost::OnInvokeMethod(
render_frame_host->Send(reply_msg);
return;
}
+ DCHECK(!HasPendingReply(render_frame_host));
+ pending_replies_[render_frame_host] = reply_msg;
scoped_refptr<GinJavaMethodInvocationHelper> result =
new GinJavaMethodInvocationHelper(
make_scoped_ptr(new GinJavaBoundObjectDelegate(object))
&GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult,
AsWeakPtr(),
render_frame_host,
- reply_msg,
result));
}
void GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult(
RenderFrameHost* render_frame_host,
- IPC::Message* reply_msg,
scoped_refptr<GinJavaMethodInvocationHelper> result) {
if (result->HoldsPrimitiveResult()) {
+ IPC::Message* reply_msg = TakePendingReply(render_frame_host);
+ if (!reply_msg) {
+ return;
+ }
IPC::WriteParam(reply_msg, result->GetPrimitiveResult());
IPC::WriteParam(reply_msg, result->GetInvocationError());
- SendReply(render_frame_host, reply_msg);
+ render_frame_host->Send(reply_msg);
} else {
- ProcessMethodInvocationObjectResult(render_frame_host, reply_msg, result);
+ ProcessMethodInvocationObjectResult(render_frame_host, result);
}
}
void GinJavaBridgeDispatcherHost::ProcessMethodInvocationObjectResult(
RenderFrameHost* render_frame_host,
- IPC::Message* reply_msg,
scoped_refptr<GinJavaMethodInvocationHelper> result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
if (!IsValidRenderFrameHost(render_frame_host)) {
- delete reply_msg;
+ // In this case, we must've already sent the reply when the render frame
+ // was destroyed.
+ DCHECK(!HasPendingReply(render_frame_host));
return;
}
+
base::ListValue wrapped_result;
if (!result->GetObjectResult().is_null()) {
GinJavaBoundObject::ObjectID returned_object_id;
render_frame_host);
}
wrapped_result.Append(
- GinJavaBridgeValue::CreateObjectIDValue(returned_object_id).release());
+ GinJavaBridgeValue::CreateObjectIDValue(
+ returned_object_id).release());
} else {
wrapped_result.Append(base::Value::CreateNullValue());
}
+ IPC::Message* reply_msg = TakePendingReply(render_frame_host);
+ if (!reply_msg) {
+ return;
+ }
IPC::WriteParam(reply_msg, wrapped_result);
IPC::WriteParam(reply_msg, result->GetInvocationError());
render_frame_host->Send(reply_msg);
}
}
+IPC::Message* GinJavaBridgeDispatcherHost::TakePendingReply(
+ RenderFrameHost* render_frame_host) {
+ if (!IsValidRenderFrameHost(render_frame_host)) {
+ DCHECK(!HasPendingReply(render_frame_host));
+ return NULL;
+ }
+
+ PendingReplyMap::iterator it = pending_replies_.find(render_frame_host);
+ // There may be no pending reply if we're called from RenderFrameDeleted and
+ // we already sent the reply through the regular route.
+ if (it == pending_replies_.end()) {
+ return NULL;
+ }
+
+ IPC::Message* reply_msg = it->second;
+ pending_replies_.erase(it);
+ return reply_msg;
+}
+
+bool GinJavaBridgeDispatcherHost::HasPendingReply(
+ RenderFrameHost* render_frame_host) const {
+ return pending_replies_.find(render_frame_host) != pending_replies_.end();
+}
+
} // namespace content
GinJavaBoundObject::ObjectID object_id);
bool IsValidRenderFrameHost(RenderFrameHost* render_frame_host);
- void SendReply(RenderFrameHost* render_frame_host, IPC::Message* reply_msg);
void SendMethods(RenderFrameHost* render_frame_host,
- IPC::Message* reply_msg,
const std::set<std::string>& method_names);
void SendHasMethodReply(RenderFrameHost* render_frame_host,
- IPC::Message* reply_msg,
bool result);
void ProcessMethodInvocationResult(
RenderFrameHost* render_frame_host,
- IPC::Message* reply_msg,
scoped_refptr<GinJavaMethodInvocationHelper> result);
void ProcessMethodInvocationObjectResult(
RenderFrameHost* render_frame_host,
- IPC::Message* reply_msg,
scoped_refptr<GinJavaMethodInvocationHelper> result);
GinJavaBoundObject::ObjectID AddObject(
const base::android::JavaRef<jobject>& object,
void RemoveHolder(RenderFrameHost* holder,
const GinJavaBoundObject::ObjectMap::iterator& from,
size_t count);
+ bool HasPendingReply(RenderFrameHost* render_frame_host) const;
+ IPC::Message* TakePendingReply(RenderFrameHost* render_frame_host);
// Every time a GinJavaBoundObject backed by a real Java object is
// created/destroyed, we insert/remove a strong ref to that Java object into
typedef std::map<std::string, GinJavaBoundObject::ObjectID> NamedObjectMap;
NamedObjectMap named_objects_;
+ // Keep track of pending calls out to Java such that we can send a synchronous
+ // reply to the renderer waiting on the response should the RenderFrame be
+ // destroyed while the reply is pending.
+ // Only used on the UI thread.
+ typedef std::map<RenderFrameHost*, IPC::Message*> PendingReplyMap;
+ PendingReplyMap pending_replies_;
+
DISALLOW_COPY_AND_ASSIGN(GinJavaBridgeDispatcherHost);
};
LOG(FATAL) << " Failed to allocate bitmap of size " << bounds.width() << "x"
<< bounds.height();
}
+ glow_bitmap.eraseColor(SK_ColorTRANSPARENT);
SkCanvas canvas(glow_bitmap);
canvas.clipRect(SkRect::MakeXYWH(0, 0, bounds.width(), bounds.height()));
RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(RenderViewHost* rvh)
: render_view_host_(NULL),
- overrides_handler_(new RendererOverridesHandler(this)),
+ overrides_handler_(new RendererOverridesHandler()),
tracing_handler_(
new DevToolsTracingHandler(DevToolsTracingHandler::Renderer)),
power_handler_(new DevToolsPowerHandler()),
if (!render_view_host_)
return;
- if (render_view_host_ == dest_rvh && static_cast<RenderViewHostImpl*>(
- render_view_host_)->render_view_termination_status() ==
+ if (render_view_host_ == dest_rvh &&
+ render_view_host_->render_view_termination_status() ==
base::TERMINATION_STATUS_STILL_RUNNING)
return;
ReattachToRenderViewHost(dest_rvh);
void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost* rvh) {
DCHECK(!render_view_host_);
- render_view_host_ = rvh;
+ render_view_host_ = static_cast<RenderViewHostImpl*>(rvh);
WebContentsObserver::Observe(WebContents::FromRenderViewHost(rvh));
- overrides_handler_->OnRenderViewHostChanged();
+ overrides_handler_->SetRenderViewHost(render_view_host_);
registrar_.Add(
this,
content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
content::Source<RenderWidgetHost>(render_view_host_));
render_view_host_ = NULL;
+ overrides_handler_->ClearRenderViewHost();
}
void RenderViewDevToolsAgentHost::DisconnectWebContents() {
class DevToolsTracingHandler;
class RendererOverridesHandler;
class RenderViewHost;
+class RenderViewHostImpl;
#if defined(OS_ANDROID)
class PowerSaveBlockerImpl;
void InnerOnClientAttached();
void InnerClientDetachedFromRenderer();
- RenderViewHost* render_view_host_;
+ RenderViewHostImpl* render_view_host_;
scoped_ptr<RendererOverridesHandler> overrides_handler_;
scoped_ptr<DevToolsTracingHandler> tracing_handler_;
scoped_ptr<DevToolsPowerHandler> power_handler_;
} // namespace
-RendererOverridesHandler::RendererOverridesHandler(DevToolsAgentHost* agent)
- : agent_(agent),
- has_last_compositor_frame_metadata_(false),
+RendererOverridesHandler::RendererOverridesHandler()
+ : has_last_compositor_frame_metadata_(false),
capture_retry_count_(0),
weak_factory_(this) {
RegisterCommandHandler(
RendererOverridesHandler::~RendererOverridesHandler() {}
void RendererOverridesHandler::OnClientDetached() {
- RenderViewHostImpl* host = GetRenderViewHostImpl();
- if (screencast_command_ && host)
- host->SetTouchEventEmulationEnabled(false, false);
+ if (screencast_command_ && host_)
+ host_->SetTouchEventEmulationEnabled(false, false);
screencast_command_ = NULL;
}
NotifyScreencastVisibility(visible);
}
-void RendererOverridesHandler::OnRenderViewHostChanged() {
- RenderViewHostImpl* host = GetRenderViewHostImpl();
+void RendererOverridesHandler::SetRenderViewHost(
+ RenderViewHostImpl* host) {
+ host_ = host;
if (screencast_command_ && host)
host->SetTouchEventEmulationEnabled(true, true);
}
+void RendererOverridesHandler::ClearRenderViewHost() {
+ host_ = NULL;
+}
+
bool RendererOverridesHandler::OnSetTouchEventEmulationEnabled() {
return screencast_command_.get() != NULL;
}
return;
}
- RenderViewHost* host = GetRenderViewHostImpl();
- if (!host->GetView())
+ if (!host_ || !host_->GetView())
return;
last_frame_time_ = base::TimeTicks::Now();
RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
- host->GetView());
+ host_->GetView());
// TODO(vkuzkokov): do not use previous frame metadata.
cc::CompositorFrameMetadata& metadata = last_compositor_frame_metadata_;
devtools::DOM::setFileInputFiles::kParamFiles;
if (!params || !params->GetList(param, &file_list))
return command->InvalidParamResponse(param);
- RenderViewHost* host = GetRenderViewHostImpl();
- if (!host)
+ if (!host_)
return NULL;
for (size_t i = 0; i < file_list->GetSize(); ++i) {
if (!file_list->GetString(i, &file))
return command->InvalidParamResponse(param);
ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
- host->GetProcess()->GetID(), base::FilePath(file));
+ host_->GetProcess()->GetID(), base::FilePath(file));
}
return NULL;
}
scoped_refptr<DevToolsProtocol::Response>
RendererOverridesHandler::ClearBrowserCache(
scoped_refptr<DevToolsProtocol::Command> command) {
- GetContentClient()->browser()->ClearCache(GetRenderViewHostImpl());
+ GetContentClient()->browser()->ClearCache(host_);
return command->SuccessResponse(NULL);
}
scoped_refptr<DevToolsProtocol::Response>
RendererOverridesHandler::ClearBrowserCookies(
scoped_refptr<DevToolsProtocol::Command> command) {
- GetContentClient()->browser()->ClearCookies(GetRenderViewHostImpl());
+ GetContentClient()->browser()->ClearCookies(host_);
return command->SuccessResponse(NULL);
}
scoped_refptr<DevToolsProtocol::Response>
RendererOverridesHandler::PageDisable(
scoped_refptr<DevToolsProtocol::Command> command) {
- RenderViewHostImpl* host = GetRenderViewHostImpl();
- if (screencast_command_ && host)
- host->SetTouchEventEmulationEnabled(false, false);
+ if (screencast_command_ && host_)
+ host_->SetTouchEventEmulationEnabled(false, false);
screencast_command_ = NULL;
return NULL;
}
prompt_override_ptr = NULL;
}
- WebContents* web_contents = agent_->GetWebContents();
+ if (!host_)
+ return command->InternalErrorResponse("Could not connect to view");
+
+ WebContents* web_contents = WebContents::FromRenderViewHost(host_);
if (web_contents) {
JavaScriptDialogManager* manager =
web_contents->GetDelegate()->GetJavaScriptDialogManager();
if (!gurl.is_valid())
return command->InternalErrorResponse("Cannot navigate to invalid URL");
- WebContents* web_contents = agent_->GetWebContents();
+ if (!host_)
+ return command->InternalErrorResponse("Could not connect to view");
+
+ WebContents* web_contents = WebContents::FromRenderViewHost(host_);
if (web_contents) {
web_contents->GetController()
.LoadURL(gurl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
scoped_refptr<DevToolsProtocol::Response>
RendererOverridesHandler::PageReload(
scoped_refptr<DevToolsProtocol::Command> command) {
- WebContents* web_contents = agent_->GetWebContents();
+ if (!host_)
+ return command->InternalErrorResponse("Could not connect to view");
+
+ WebContents* web_contents = WebContents::FromRenderViewHost(host_);
if (web_contents) {
// Override only if it is crashed.
if (!web_contents->IsCrashed())
scoped_refptr<DevToolsProtocol::Response>
RendererOverridesHandler::PageGetNavigationHistory(
scoped_refptr<DevToolsProtocol::Command> command) {
- WebContents* web_contents = agent_->GetWebContents();
+ if (!host_)
+ return command->InternalErrorResponse("Could not connect to view");
+ WebContents* web_contents = WebContents::FromRenderViewHost(host_);
if (web_contents) {
base::DictionaryValue* result = new base::DictionaryValue();
NavigationController& controller = web_contents->GetController();
return command->InvalidParamResponse(param);
}
- WebContents* web_contents = agent_->GetWebContents();
+ if (!host_)
+ return command->InternalErrorResponse("Could not connect to view");
+
+ WebContents* web_contents = WebContents::FromRenderViewHost(host_);
if (web_contents) {
NavigationController& controller = web_contents->GetController();
for (int i = 0; i != controller.GetEntryCount(); ++i) {
scoped_refptr<DevToolsProtocol::Response>
RendererOverridesHandler::PageCaptureScreenshot(
scoped_refptr<DevToolsProtocol::Command> command) {
- RenderViewHostImpl* host = GetRenderViewHostImpl();
- if (!host->GetView())
- return command->InternalErrorResponse("Unable to access the view");
+ if (!host_ || !host_->GetView())
+ return command->InternalErrorResponse("Could not connect to view");
- host->GetSnapshotFromBrowser(
+ host_->GetSnapshotFromBrowser(
base::Bind(&RendererOverridesHandler::ScreenshotCaptured,
weak_factory_.GetWeakPtr(), command));
return command->AsyncResponsePromise();
RendererOverridesHandler::PageStartScreencast(
scoped_refptr<DevToolsProtocol::Command> command) {
screencast_command_ = command;
- RenderViewHostImpl* host = GetRenderViewHostImpl();
- host->SetTouchEventEmulationEnabled(true, true);
- bool visible = !host->is_hidden();
+ if (!host_)
+ return command->InternalErrorResponse("Could not connect to view");
+ host_->SetTouchEventEmulationEnabled(true, true);
+ bool visible = !host_->is_hidden();
NotifyScreencastVisibility(visible);
if (visible) {
if (has_last_compositor_frame_metadata_)
InnerSwapCompositorFrame();
else
- host->Send(new ViewMsg_ForceRedraw(host->GetRoutingID(), 0));
+ host_->Send(new ViewMsg_ForceRedraw(host_->GetRoutingID(), 0));
}
return command->SuccessResponse(NULL);
}
scoped_refptr<DevToolsProtocol::Command> command) {
last_frame_time_ = base::TimeTicks();
screencast_command_ = NULL;
- RenderViewHostImpl* host = GetRenderViewHostImpl();
- if (host)
- host->SetTouchEventEmulationEnabled(false, false);
+ if (host_)
+ host_->SetTouchEventEmulationEnabled(false, false);
return command->SuccessResponse(NULL);
}
weak_factory_.GetWeakPtr(),
command);
- scoped_refptr<quota::QuotaManager> quota_manager = GetRenderViewHostImpl()
- ->GetProcess()
- ->GetStoragePartition()
- ->GetQuotaManager();
+ if (!host_)
+ return command->InternalErrorResponse("Could not connect to view");
+
+ scoped_refptr<quota::QuotaManager> quota_manager =
+ host_->GetProcess()->GetStoragePartition()->GetQuotaManager();
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
devtools::Input::emulateTouchFromMouseEvent::kParamButton);
}
- RenderViewHost* host = GetRenderViewHostImpl();
+ if (!host_)
+ return command->InternalErrorResponse("Could not connect to view");
+
if (event->type == WebInputEvent::MouseWheel)
- host->ForwardWheelEvent(wheel_event);
+ host_->ForwardWheelEvent(wheel_event);
else
- host->ForwardMouseEvent(mouse_event);
+ host_->ForwardMouseEvent(mouse_event);
return command->SuccessResponse(NULL);
}
-RenderViewHostImpl* RendererOverridesHandler::GetRenderViewHostImpl() {
- return static_cast<RenderViewHostImpl*>(
- agent_->GetWebContents()->GetRenderViewHost());
-}
-
} // namespace content
namespace content {
-class DevToolsAgentHost;
class DevToolsTracingHandler;
class RenderViewHostImpl;
class CONTENT_EXPORT RendererOverridesHandler
: public DevToolsProtocol::Handler {
public:
- explicit RendererOverridesHandler(DevToolsAgentHost* agent);
+ RendererOverridesHandler();
virtual ~RendererOverridesHandler();
void OnClientDetached();
void OnSwapCompositorFrame(const cc::CompositorFrameMetadata& frame_metadata);
void OnVisibilityChanged(bool visible);
- void OnRenderViewHostChanged();
+ void SetRenderViewHost(RenderViewHostImpl* host);
+ void ClearRenderViewHost();
bool OnSetTouchEventEmulationEnabled();
private:
scoped_refptr<DevToolsProtocol::Response> InputEmulateTouchFromMouseEvent(
scoped_refptr<DevToolsProtocol::Command> command);
- RenderViewHostImpl* GetRenderViewHostImpl();
-
- DevToolsAgentHost* agent_;
+ RenderViewHostImpl* host_;
scoped_refptr<DevToolsProtocol::Command> screencast_command_;
bool has_last_compositor_frame_metadata_;
cc::CompositorFrameMetadata last_compositor_frame_metadata_;
// Decode database id (in iterator value).
int64 database_id = 0;
StringPiece valueSlice(it->Value());
- if (!DecodeVarInt(&valueSlice, &database_id) || !valueSlice.empty()) {
+ if (!DecodeInt(&valueSlice, &database_id) || !valueSlice.empty()) {
INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
continue;
}
uuid = base::GenerateGUID();
scoped_refptr<webkit_blob::BlobData> blob_data =
new webkit_blob::BlobData(uuid);
+ blob_data->set_content_type(base::UTF16ToUTF8(blob_info.type()));
blob_data->AppendFile(
blob_info.file_path(), 0, blob_info.size(), blob_info.last_modified());
scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle(
// <0, 0, 0, 100, database id>
// => Existence implies the database id is in the free list
// [DatabaseFreeListKey]
-// <0, 0, 0, 201, origin, database name> => Database id [DatabaseNameKey]
+// <0, 0, 0, 201, origin, database name> => Database id (int) [DatabaseNameKey]
//
//
// Database metadata: [DatabaseMetaDataKey]
Send(new MediaPlayerMsg_PauseVideo(RoutingID()));
}
+void BrowserMediaPlayerManager::ReleaseAllMediaPlayers() {
+ for (ScopedVector<MediaPlayerAndroid>::iterator it = players_.begin();
+ it != players_.end(); ++it) {
+ if ((*it)->player_id() == fullscreen_player_id_)
+ fullscreen_player_is_released_ = true;
+ (*it)->Release();
+ }
+}
+
void BrowserMediaPlayerManager::OnSeekComplete(
int player_id,
const base::TimeDelta& current_time) {
// Pauses all video players manages by this class.
void PauseVideo();
+ // Stops and releases every media managed by this class.
+ void ReleaseAllMediaPlayers();
+
// media::MediaPlayerManager overrides.
virtual void OnTimeUpdate(
int player_id, base::TimeDelta current_time) OVERRIDE;
#include "content/browser/media/capture/desktop_capture_device_uma_types.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/desktop_media_id.h"
+#include "content/public/browser/power_save_blocker.h"
#include "media/base/video_util.h"
#include "third_party/libyuv/include/libyuv/scale_argb.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_and_cursor_composer.h"
scoped_ptr<webrtc::BasicDesktopFrame> black_frame_;
+ // TODO(jiayl): Remove power_save_blocker_ when there is an API to keep the
+ // screen from sleeping for the drive-by web.
+ scoped_ptr<PowerSaveBlocker> power_save_blocker_;
+
DISALLOW_COPY_AND_ASSIGN(Core);
};
// This capturer always outputs ARGB, non-interlaced.
capture_format_.pixel_format = media::PIXEL_FORMAT_ARGB;
+ power_save_blocker_.reset(PowerSaveBlocker::Create(
+ PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep,
+ "DesktopCaptureDevice is running").release());
+
desktop_capturer_->Start(this);
CaptureFrameAndScheduleNext();
#include "content/browser/media/capture/desktop_capture_device_uma_types.h"
#include "content/common/gpu/client/gl_helper.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/power_save_blocker.h"
#include "media/base/video_util.h"
#include "media/video/capture/video_capture_types.h"
#include "skia/ext/image_operations.h"
gfx::Point cursor_hot_point_;
SkBitmap scaled_cursor_bitmap_;
+ // TODO(jiayl): Remove power_save_blocker_ when there is an API to keep the
+ // screen from sleeping for the drive-by web.
+ scoped_ptr<PowerSaveBlocker> power_save_blocker_;
+
DISALLOW_COPY_AND_ASSIGN(DesktopVideoCaptureMachine);
};
if (desktop_window_->GetHost())
desktop_window_->GetHost()->compositor()->AddObserver(this);
+ power_save_blocker_.reset(PowerSaveBlocker::Create(
+ PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep,
+ "DesktopCaptureDevice is running").release());
+
// Starts timer.
timer_.Start(FROM_HERE, oracle_proxy_->min_capture_period(),
base::Bind(&DesktopVideoCaptureMachine::Capture, AsWeakPtr(),
void DesktopVideoCaptureMachine::Stop(const base::Closure& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ power_save_blocker_.reset();
// Stop observing compositor and window events.
if (desktop_window_) {
}
void CompositingIOSurfaceLayerHelper::TimerFired() {
- SetNeedsDisplayAndDisplayAndAck();
+ DisplayIfNeededAndAck();
}
void CompositingIOSurfaceLayerHelper::BeginPumpingFrames() {
void CompositorImpl::OnLostResources() {
client_->DidLoseResources();
- ui_resource_provider_.UIResourcesAreInvalid();
}
void CompositorImpl::ScheduleComposite() {
#include "content/browser/renderer_host/input/touch_selection_controller.h"
+#include "base/auto_reset.h"
#include "base/logging.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
TouchSelectionController::TouchSelectionController(
TouchSelectionControllerClient* client)
: client_(client),
- last_input_event_type_(INPUT_EVENT_TYPE_NONE),
+ response_pending_input_event_(INPUT_EVENT_TYPE_NONE),
start_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED),
start_visible_(false),
end_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED),
const gfx::RectF& end_rect,
TouchHandleOrientation end_orientation,
bool end_visible) {
- if (!activate_selection_automatically_ && !activate_insertion_automatically_)
+ if (!activate_selection_automatically_ &&
+ !activate_insertion_automatically_) {
+ DCHECK_EQ(INPUT_EVENT_TYPE_NONE, response_pending_input_event_);
return;
+ }
if (start_rect_ == start_rect && end_rect_ == end_rect &&
start_orientation_ == start_orientation &&
end_orientation_ = end_orientation;
end_visible_ = end_visible;
+ // Ensure that |response_pending_input_event_| is cleared after the method
+ // completes, while also making its current value available for the duration
+ // of the call.
+ InputEventType causal_input_event = response_pending_input_event_;
+ response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
+ base::AutoReset<InputEventType> auto_reset_response_pending_input_event(
+ &response_pending_input_event_, causal_input_event);
+
const bool is_selection_dragging =
is_selection_active_ && (start_selection_handle_->is_dragging() ||
end_selection_handle_->is_dragging());
}
void TouchSelectionController::OnLongPressEvent() {
- last_input_event_type_ = LONG_PRESS;
+ response_pending_input_event_ = LONG_PRESS;
ShowSelectionHandlesAutomatically();
ShowInsertionHandleAutomatically();
ResetCachedValuesIfInactive();
}
void TouchSelectionController::OnTapEvent() {
- last_input_event_type_ = TAP;
+ response_pending_input_event_ = TAP;
activate_selection_automatically_ = false;
DeactivateSelection();
ShowInsertionHandleAutomatically();
}
void TouchSelectionController::HideAndDisallowShowingAutomatically() {
- last_input_event_type_ = INPUT_EVENT_TYPE_NONE;
+ response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
DeactivateInsertion();
DeactivateSelection();
activate_insertion_automatically_ = false;
void TouchSelectionController::OnInsertionChanged() {
DeactivateSelection();
- if (last_input_event_type_ == TAP && selection_empty_) {
+ if (response_pending_input_event_ == TAP && selection_empty_) {
HideAndDisallowShowingAutomatically();
return;
}
end_selection_handle_->SetOrientation(end_orientation_);
}
- if (!is_selection_active_) {
+ // As a long press received while a selection is already active may trigger
+ // an entirely new selection, notify the client but avoid sending an
+ // intervening SELECTION_CLEARED update to avoid unnecessary state changes.
+ if (!is_selection_active_ || response_pending_input_event_ == LONG_PRESS) {
is_selection_active_ = true;
+ response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
client_->OnSelectionEvent(SELECTION_SHOWN, GetStartPosition());
}
}
TouchSelectionControllerClient* const client_;
- InputEventType last_input_event_type_;
+ InputEventType response_pending_input_event_;
gfx::RectF start_rect_;
TouchHandleOrientation start_orientation_;
EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
}
+TEST_F(TouchSelectionControllerTest, SelectionRepeatedLongPress) {
+ gfx::RectF start_rect(5, 5, 0, 10);
+ gfx::RectF end_rect(50, 5, 0, 10);
+ bool visible = true;
+
+ controller().OnLongPressEvent();
+ ChangeSelection(start_rect, visible, end_rect, visible);
+ EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
+ EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
+
+ // A long press triggering a new selection should re-send the SELECTION_SHOWN
+ // event notification.
+ start_rect.Offset(10, 10);
+ controller().OnLongPressEvent();
+ ChangeSelection(start_rect, visible, end_rect, visible);
+ EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
+ EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
+}
+
TEST_F(TouchSelectionControllerTest, SelectionDragged) {
base::TimeTicks event_time = base::TimeTicks::Now();
controller().OnLongPressEvent();
#include "media/audio/audio_manager_base.h"
#include "media/base/audio_bus.h"
+namespace {
+
+void LogMessage(int stream_id, const std::string& msg, bool add_prefix) {
+ std::ostringstream oss;
+ oss << "[stream_id=" << stream_id << "] ";
+ if (add_prefix)
+ oss << "AIRH::";
+ oss << msg;
+ content::MediaStreamManager::SendMessageToNativeLog(oss.str());
+ DVLOG(1) << oss.str();
+}
+
+}
+
namespace content {
struct AudioInputRendererHost::AudioEntry {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
AudioEntry* entry = LookupByController(controller);
- if (!entry)
+ if (!entry) {
+ NOTREACHED() << "AudioInputController is invalid.";
return;
+ }
if (!PeerHandle()) {
NOTREACHED() << "Renderer process handle is invalid.";
return;
}
+ LogMessage(entry->stream_id,
+ "DoCompleteCreation => IPC channel and stream are now open",
+ true);
+
Send(new AudioInputMsg_NotifyStreamCreated(entry->stream_id,
foreign_memory_handle, foreign_socket_handle,
entry->shared_memory.requested_size(),
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// TODO(henrika): See crbug.com/115262 for details on why this method
// should be implemented.
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry) {
+ NOTREACHED() << "AudioInputController is invalid.";
+ return;
+ }
+ LogMessage(entry->stream_id,
+ "DoSendRecordingMessage => stream is now started",
+ true);
}
void AudioInputRendererHost::DoHandleError(
media::AudioInputController* controller,
media::AudioInputController::ErrorCode error_code) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
- // Log all errors even it is ignored later.
- MediaStreamManager::SendMessageToNativeLog(
- base::StringPrintf("AudioInputController error: %d", error_code));
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry) {
+ NOTREACHED() << "AudioInputController is invalid.";
+ return;
+ }
// This is a fix for crbug.com/357501. The error can be triggered when closing
// the lid on Macs, which causes more problems than it fixes.
// Also, in crbug.com/357569, the goal is to remove usage of the error since
// it was added to solve a crash on Windows that no longer can be reproduced.
if (error_code == media::AudioInputController::NO_DATA_ERROR) {
- DVLOG(1) << "AudioInputRendererHost@" << this << "::DoHandleError: "
- << "NO_DATA_ERROR ignored.";
+ // TODO(henrika): it might be possible to do something other than just
+ // logging when we detect many NO_DATA_ERROR calls for a stream.
+ LogMessage(entry->stream_id, "AIC => NO_DATA_ERROR", false);
return;
}
- AudioEntry* entry = LookupByController(controller);
- if (!entry)
- return;
+ std::ostringstream oss;
+ oss << "AIC reports error_code=" << error_code;
+ LogMessage(entry->stream_id, oss.str(), false);
audio_log_->OnError(entry->stream_id);
DeleteEntryOnError(entry, AUDIO_INPUT_CONTROLLER_ERROR);
const std::string& message) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
AudioEntry* entry = LookupByController(controller);
- if (!entry)
+ if (!entry) {
+ NOTREACHED() << "AudioInputController is invalid.";
return;
+ }
// Add stream ID and current audio level reported by AIC to native log.
- std::string log_string =
- base::StringPrintf("[stream_id=%d] ", entry->stream_id);
- log_string += message;
- MediaStreamManager::SendMessageToNativeLog(log_string);
- DVLOG(1) << log_string;
+ LogMessage(entry->stream_id, message, false);
}
bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message) {
const AudioInputHostMsg_CreateStream_Config& config) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
- DVLOG(1) << "AudioInputRendererHost@" << this
- << "::OnCreateStream(stream_id=" << stream_id
- << ", render_view_id=" << render_view_id
- << ", session_id=" << session_id << ")";
+ std::ostringstream oss;
+ oss << "[stream_id=" << stream_id << "] "
+ << "AIRH::OnCreateStream(render_view_id=" << render_view_id
+ << ", session_id=" << session_id << ")";
DCHECK_GT(render_view_id, 0);
// media::AudioParameters is validated in the deserializer.
device_id = info->device.id;
device_name = info->device.name;
+ oss << ": device_name=" << device_name;
}
// Create a new AudioEntry structure.
// Set the initial AGC state for the audio input stream. Note that, the AGC
// is only supported in AUDIO_PCM_LOW_LATENCY mode.
- if (config.params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY)
+ if (config.params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY) {
entry->controller->SetAutomaticGainControl(config.automatic_gain_control);
+ oss << ", AGC=" << config.automatic_gain_control;
+ }
+
+ MediaStreamManager::SendMessageToNativeLog(oss.str());
+ DVLOG(1) << oss.str();
// Since the controller was created successfully, create an entry and add it
// to the map.
entry->stream_id = stream_id;
audio_entries_.insert(std::make_pair(stream_id, entry.release()));
-
- MediaStreamManager::SendMessageToNativeLog(
- "Audio input stream created successfully. Device name: " + device_name);
audio_log_->OnCreated(stream_id, audio_params, device_id);
}
void AudioInputRendererHost::OnRecordStream(int stream_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ LogMessage(stream_id, "OnRecordStream", true);
AudioEntry* entry = LookupById(stream_id);
if (!entry) {
void AudioInputRendererHost::OnCloseStream(int stream_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ LogMessage(stream_id, "OnCloseStream", true);
AudioEntry* entry = LookupById(stream_id);
void AudioInputRendererHost::SendErrorMessage(
int stream_id, ErrorCode error_code) {
- MediaStreamManager::SendMessageToNativeLog(
- base::StringPrintf("AudioInputRendererHost error: %d", error_code));
+ std::string err_msg =
+ base::StringPrintf("SendErrorMessage(error_code=%d)", error_code);
+ LogMessage(stream_id, err_msg, true);
+
Send(new AudioInputMsg_NotifyStreamStateChanged(
stream_id, media::AudioInputIPCDelegate::kError));
}
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!entry->pending_close) {
+ LogMessage(entry->stream_id, "CloseAndDeleteStream", true);
entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry,
this, entry));
entry->pending_close = true;
void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ LogMessage(entry->stream_id, "DeleteEntry => stream is now closed", true);
// Delete the entry when this method goes out of scope.
scoped_ptr<AudioEntry> entry_deleter(entry);
if (last_write_time_.is_null()) {
// This is the first time Write is called.
base::TimeDelta interval = base::Time::Now() - creation_time_;
- oss << "Audio input data received for the first time: delay = "
- << interval.InMilliseconds() << "ms.";
+ oss << "AISW::Write => audio input data received for the first time: delay "
+ "= " << interval.InMilliseconds() << "ms";
} else {
base::TimeDelta interval = base::Time::Now() - last_write_time_;
if (interval > kLogDelayThreadhold) {
- oss << "Audio input data delay unexpectedly long: delay = "
- << interval.InMilliseconds() << "ms.";
+ oss << "AISW::Write => audio input data delay unexpectedly long: delay = "
+ << interval.InMilliseconds() << "ms";
}
}
- if (!oss.str().empty())
+ if (!oss.str().empty()) {
MediaStreamManager::SendMessageToNativeLog(oss.str());
+ DVLOG(1) << oss.str();
+ }
last_write_time_ = base::Time::Now();
#endif
#include "base/command_line.h"
#include "base/memory/shared_memory.h"
#include "base/metrics/histogram.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/public/common/content_switches.h"
#include "media/audio/audio_buffers_state.h"
#include "media/audio/audio_parameters.h"
using media::AudioBus;
+namespace {
+
+// Used to log if any audio glitches have been detected during an audio session.
+// Elements in this enum should not be added, deleted or rearranged.
+enum AudioGlitchResult {
+ AUDIO_RENDERER_NO_AUDIO_GLITCHES = 0,
+ AUDIO_RENDERER_AUDIO_GLITCHES = 1,
+ AUDIO_RENDERER_AUDIO_GLITCHES_MAX = AUDIO_RENDERER_AUDIO_GLITCHES
+};
+
+void LogAudioGlitchResult(AudioGlitchResult result) {
+ UMA_HISTOGRAM_ENUMERATION("Media.AudioRendererAudioGlitches",
+ result,
+ AUDIO_RENDERER_AUDIO_GLITCHES_MAX + 1);
+}
+
+} // namespace
+
namespace content {
AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory,
100.0 * renderer_missed_callback_count_ / renderer_callback_count_;
UMA_HISTOGRAM_PERCENTAGE(
"Media.AudioRendererMissedDeadline", percentage_missed);
+
+ // Add more detailed information regarding detected audio glitches where
+ // a non-zero value of |renderer_missed_callback_count_| is added to the
+ // AUDIO_RENDERER_AUDIO_GLITCHES bin.
+ renderer_missed_callback_count_ > 0 ?
+ LogAudioGlitchResult(AUDIO_RENDERER_AUDIO_GLITCHES) :
+ LogAudioGlitchResult(AUDIO_RENDERER_NO_AUDIO_GLITCHES);
+ std::string log_string =
+ base::StringPrintf("ASR: number of detected audio glitches=%d",
+ static_cast<int>(renderer_missed_callback_count_));
+ MediaStreamManager::SendMessageToNativeLog(log_string);
+ DVLOG(1) << log_string;
}
// media::AudioOutputController::SyncReader implementations.
switches::kDisableWebRtcHWDecoding,
switches::kDisableWebRtcHWEncoding,
switches::kEnableWebRtcHWVp8Encoding,
+ switches::kEnableWebRtcHWH264Encoding,
#endif
switches::kLowEndDeviceMode,
#if defined(OS_ANDROID)
}
RenderWidgetHostImpl::~RenderWidgetHostImpl() {
+ if (view_weak_)
+ view_weak_->RenderWidgetHostGone();
SetView(NULL);
GpuSurfaceTracker::Get()->RemoveSurface(surface_id_);
}
void RenderWidgetHostImpl::SetView(RenderWidgetHostViewBase* view) {
+ if (view)
+ view_weak_ = view->GetWeakPtr();
+ else
+ view_weak_.reset();
view_ = view;
GpuSurfaceTracker::Get()->SetSurfaceHandle(
// We need to at least make sure that the RenderProcessHost is notified about
// the |is_hidden_| change, so that the renderer will have correct visibility
// set when respawned.
- if (!is_hidden_) {
+ if (is_hidden_) {
process_->WidgetRestored();
is_hidden_ = false;
}
GpuSurfaceTracker::Get()->SetSurfaceHandle(surface_id_,
gfx::GLSurfaceHandle());
view_->RenderProcessGone(status, exit_code);
- view_ = NULL; // The View should be deleted by RenderProcessGone.
+ view_ = NULL; // The View should be deleted by RenderProcessGone.
+ view_weak_.reset();
}
// Reconstruct the input router to ensure that it has fresh state for a new
// doing so).
RenderWidgetHostViewBase* view_;
+ // A weak pointer to the view. The above pointer should be weak, but changing
+ // that to be weak causes crashes on Android.
+ // TODO(ccameron): Fix this.
+ // http://crbug.com/404828
+ base::WeakPtr<RenderWidgetHostViewBase> view_weak_;
+
// true if a renderer has once been valid. We use this flag to display a sad
// tab only when we lose our renderer and not if a paint occurs during
// initialization.
ASSERT_FALSE(host_->input_router()->HasPendingEvents());
}
+// Regression test for http://crbug.com/401859.
+TEST_F(RenderWidgetHostTest, RendererExitedResetsIsHidden) {
+ // RendererExited will delete the view.
+ host_->SetView(new TestView(host_.get()));
+ host_->WasHidden();
+
+ ASSERT_TRUE(host_->is_hidden());
+ host_->RendererExited(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
+ ASSERT_FALSE(host_->is_hidden());
+
+ // Make sure the input router is in a fresh state.
+ ASSERT_FALSE(host_->input_router()->HasPendingEvents());
+}
+
} // namespace content
TRACE_EVENT0(
"cc", "RenderWidgetHostViewAndroid::CopyFromCompositingSurfaceFinished");
bitmap_pixels_lock.reset();
- release_callback->Run(0, false);
+ uint32 sync_point = 0;
+ if (result) {
+ GLHelper* gl_helper =
+ ImageTransportFactoryAndroid::GetInstance()->GetGLHelper();
+ sync_point = gl_helper->InsertSyncPoint();
+ }
+ bool lost_resource = sync_point == 0;
+ release_callback->Run(sync_point, lost_resource);
UMA_HISTOGRAM_TIMES(kAsyncReadBackString,
base::TimeTicks::Now() - start_time);
callback.Run(result, *bitmap);
RenderWidgetHostImpl* widget_host,
ContentViewCoreImpl* content_view_core)
: host_(widget_host),
- needs_begin_frame_(false),
+ outstanding_vsync_requests_(0),
is_showing_(!widget_host->is_hidden()),
content_view_core_(NULL),
ime_adapter_android_(this),
gesture_text_selector_(this),
touch_scrolling_(false),
potentially_active_fling_count_(0),
- flush_input_requested_(false),
accelerated_surface_route_id_(0),
using_synchronous_compositor_(SynchronousCompositorImpl::FromID(
widget_host->GetProcess()->GetID(),
host_->WasShown(ui::LatencyInfo());
- if (content_view_core_ && !using_synchronous_compositor_) {
- content_view_core_->GetWindowAndroid()->AddObserver(this);
- content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
- observing_root_window_ = true;
+ if (content_view_core_) {
+ StartObservingRootWindow();
+ RequestVSyncUpdate(BEGIN_FRAME);
}
}
// utilization.
host_->WasHidden();
- if (content_view_core_ && !using_synchronous_compositor_) {
- content_view_core_->GetWindowAndroid()->RemoveObserver(this);
- observing_root_window_ = false;
- }
+ StopObservingRootWindow();
}
void RenderWidgetHostViewAndroid::WasResized() {
}
void RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame(bool enabled) {
- if (enabled == needs_begin_frame_)
- return;
-
+ DCHECK(!using_synchronous_compositor_);
TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame",
"enabled", enabled);
- if (content_view_core_ && enabled)
- content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
-
- needs_begin_frame_ = enabled;
+ if (enabled)
+ RequestVSyncUpdate(PERSISTENT_BEGIN_FRAME);
+ else
+ outstanding_vsync_requests_ &= ~PERSISTENT_BEGIN_FRAME;
}
void RenderWidgetHostViewAndroid::OnStartContentIntent(
overscroll_effect_->Disable();
}
+void RenderWidgetHostViewAndroid::RequestVSyncUpdate(uint32 requests) {
+ // The synchronous compositor does not requre BeginFrame messages.
+ if (using_synchronous_compositor_)
+ requests &= FLUSH_INPUT;
+
+ bool should_request_vsync = !outstanding_vsync_requests_ && requests;
+ outstanding_vsync_requests_ |= requests;
+ // Note that if we're not currently observing the root window, outstanding
+ // vsync requests will be pushed if/when we resume observing in
+ // |StartObservingRootWindow()|.
+ if (observing_root_window_ && should_request_vsync)
+ content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
+}
+
+void RenderWidgetHostViewAndroid::StartObservingRootWindow() {
+ DCHECK(content_view_core_);
+ if (observing_root_window_)
+ return;
+
+ observing_root_window_ = true;
+ content_view_core_->GetWindowAndroid()->AddObserver(this);
+
+ // Clear existing vsync requests to allow a request to the new window.
+ uint32 outstanding_vsync_requests = outstanding_vsync_requests_;
+ outstanding_vsync_requests_ = 0;
+ RequestVSyncUpdate(outstanding_vsync_requests);
+}
+
+void RenderWidgetHostViewAndroid::StopObservingRootWindow() {
+ if (!content_view_core_) {
+ DCHECK(!observing_root_window_);
+ return;
+ }
+
+ if (!observing_root_window_)
+ return;
+
+ observing_root_window_ = false;
+ content_view_core_->GetWindowAndroid()->RemoveObserver(this);
+}
+
+void RenderWidgetHostViewAndroid::SendBeginFrame(base::TimeTicks frame_time,
+ base::TimeDelta vsync_period) {
+ TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::SendBeginFrame");
+ base::TimeTicks display_time = frame_time + vsync_period;
+
+ // TODO(brianderson): Use adaptive draw-time estimation.
+ base::TimeDelta estimated_browser_composite_time =
+ base::TimeDelta::FromMicroseconds(
+ (1.0f * base::Time::kMicrosecondsPerSecond) / (3.0f * 60));
+
+ base::TimeTicks deadline = display_time - estimated_browser_composite_time;
+
+ host_->Send(new ViewMsg_BeginFrame(
+ host_->GetRoutingID(),
+ cc::BeginFrameArgs::Create(frame_time, deadline, vsync_period)));
+}
+
bool RenderWidgetHostViewAndroid::Animate(base::TimeTicks frame_time) {
bool needs_animate =
overscroll_effect_ ? overscroll_effect_->Animate(frame_time) : false;
}
void RenderWidgetHostViewAndroid::OnSetNeedsFlushInput() {
- if (flush_input_requested_ || !content_view_core_)
- return;
TRACE_EVENT0("input", "RenderWidgetHostViewAndroid::OnSetNeedsFlushInput");
- flush_input_requested_ = true;
- content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
+ RequestVSyncUpdate(FLUSH_INPUT);
}
BrowserAccessibilityManager*
// This is good enough as long as the first touch event has Begin semantics
// and the actual scroll happens on the next vsync.
// TODO: Is this actually still needed?
- if (content_view_core_ && observing_root_window_) {
- content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
- }
+ if (observing_root_window_)
+ RequestVSyncUpdate(BEGIN_FRAME);
}
void RenderWidgetHostViewAndroid::SendMouseEvent(
void RenderWidgetHostViewAndroid::SetContentViewCore(
ContentViewCoreImpl* content_view_core) {
RemoveLayers();
- if (observing_root_window_ && content_view_core_) {
- content_view_core_->GetWindowAndroid()->RemoveObserver(this);
- observing_root_window_ = false;
- }
+ StopObservingRootWindow();
bool resize = false;
if (content_view_core != content_view_core_) {
+ overscroll_effect_.reset();
selection_controller_.reset();
ReleaseLocksOnSurface();
resize = true;
if (!content_view_core_)
return;
- if (!using_synchronous_compositor_) {
- content_view_core_->GetWindowAndroid()->AddObserver(this);
- observing_root_window_ = true;
- if (needs_begin_frame_)
- content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
- }
+ StartObservingRootWindow();
if (resize)
WasResized();
if (!selection_controller_)
selection_controller_.reset(new TouchSelectionController(this));
- if (!content_view_core_)
- overscroll_effect_.reset();
- else if (overscroll_effect_enabled_ && !overscroll_effect_)
+ if (overscroll_effect_enabled_ && !overscroll_effect_ &&
+ content_view_core_->GetWindowAndroid()->GetCompositor())
overscroll_effect_ = CreateOverscrollEffect(content_view_core_);
}
RunAckCallbacks();
}
+
+void RenderWidgetHostViewAndroid::OnAttachCompositor() {
+ DCHECK(content_view_core_);
+ if (overscroll_effect_enabled_ && !overscroll_effect_)
+ overscroll_effect_ = CreateOverscrollEffect(content_view_core_);
+}
+
void RenderWidgetHostViewAndroid::OnDetachCompositor() {
DCHECK(content_view_core_);
DCHECK(!using_synchronous_compositor_);
RunAckCallbacks();
+ overscroll_effect_.reset();
}
void RenderWidgetHostViewAndroid::OnVSync(base::TimeTicks frame_time,
if (!host_)
return;
- if (flush_input_requested_) {
- flush_input_requested_ = false;
- host_->FlushInput();
- }
-
- TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::SendBeginFrame");
- base::TimeTicks display_time = frame_time + vsync_period;
+ const uint32 current_vsync_requests = outstanding_vsync_requests_;
+ outstanding_vsync_requests_ = 0;
- // TODO(brianderson): Use adaptive draw-time estimation.
- base::TimeDelta estimated_browser_composite_time =
- base::TimeDelta::FromMicroseconds(
- (1.0f * base::Time::kMicrosecondsPerSecond) / (3.0f * 60));
-
- base::TimeTicks deadline = display_time - estimated_browser_composite_time;
+ if (current_vsync_requests & FLUSH_INPUT)
+ host_->FlushInput();
- host_->Send(new ViewMsg_BeginFrame(
- host_->GetRoutingID(),
- cc::BeginFrameArgs::Create(frame_time, deadline, vsync_period)));
+ if (current_vsync_requests & BEGIN_FRAME ||
+ current_vsync_requests & PERSISTENT_BEGIN_FRAME) {
+ SendBeginFrame(frame_time, vsync_period);
+ }
- if (needs_begin_frame_)
- content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
+ if (current_vsync_requests & PERSISTENT_BEGIN_FRAME)
+ RequestVSyncUpdate(PERSISTENT_BEGIN_FRAME);
}
void RenderWidgetHostViewAndroid::OnAnimate(base::TimeTicks begin_frame_time) {
// ui::WindowAndroidObserver implementation.
virtual void OnCompositingDidCommit() OVERRIDE;
- virtual void OnAttachCompositor() OVERRIDE {}
+ virtual void OnAttachCompositor() OVERRIDE;
virtual void OnDetachCompositor() OVERRIDE;
virtual void OnVSync(base::TimeTicks frame_time,
base::TimeDelta vsync_period) OVERRIDE;
void InternalSwapCompositorFrame(uint32 output_surface_id,
scoped_ptr<cc::CompositorFrame> frame);
+ enum VSyncRequestType {
+ FLUSH_INPUT = 1 << 0,
+ BEGIN_FRAME = 1 << 1,
+ PERSISTENT_BEGIN_FRAME = 1 << 2
+ };
+ void RequestVSyncUpdate(uint32 requests);
+ void StartObservingRootWindow();
+ void StopObservingRootWindow();
+ void SendBeginFrame(base::TimeTicks frame_time, base::TimeDelta vsync_period);
bool Animate(base::TimeTicks frame_time);
void OnContentScrollingChange();
// The model object.
RenderWidgetHostImpl* host_;
- // Used to track whether this render widget needs a BeginFrame.
- bool needs_begin_frame_;
+ // Used to control action dispatch at the next |OnVSync()| call.
+ uint32 outstanding_vsync_requests_;
bool is_showing_;
bool touch_scrolling_;
size_t potentially_active_fling_count_;
- bool flush_input_requested_;
-
int accelerated_surface_route_id_;
// Size to use if we have no backing ContentViewCore
current_device_scale_factor_(0),
current_display_rotation_(gfx::Display::ROTATE_0),
pinch_zoom_enabled_(content::IsPinchToZoomEnabled()),
- renderer_frame_number_(0) {
+ renderer_frame_number_(0),
+ weak_factory_(this) {
}
RenderWidgetHostViewBase::~RenderWidgetHostViewBase() {
return true;
}
+base::WeakPtr<RenderWidgetHostViewBase> RenderWidgetHostViewBase::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
scoped_ptr<SyntheticGestureTarget>
RenderWidgetHostViewBase::CreateSyntheticGestureTarget() {
RenderWidgetHostImpl* host =
// changed since the last time.
bool HasDisplayPropertyChanged(gfx::NativeView view);
+ base::WeakPtr<RenderWidgetHostViewBase> GetWeakPtr();
+
//----------------------------------------------------------------------------
// The following methods can be overridden by derived classes.
virtual void RenderProcessGone(base::TerminationStatus status,
int error_code) = 0;
+ // Notifies the View that the renderer's host has ceased to exist.
+ // The default implementation of this is a no-op. This hack exists to fix
+ // a crash on the branch.
+ // TODO(ccameron): Clean this up.
+ // http://crbug.com/404828
+ virtual void RenderWidgetHostGone() {}
+
// Tells the View to destroy itself.
virtual void Destroy() = 0;
base::OneShotTimer<RenderWidgetHostViewBase> flush_input_timer_;
+ base::WeakPtrFactory<RenderWidgetHostViewBase> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewBase);
};
SkBitmap bitmap;
bitmap.allocN32Pixels(video_frame->visible_rect().width(),
video_frame->visible_rect().height());
- bitmap.eraseColor(SK_ColorTRANSPARENT);
+ // Don't clear the canvas because drawing a video frame by Src mode.
SkCanvas canvas(bitmap);
-
- video_renderer.Paint(video_frame.get(),
- &canvas,
- video_frame->visible_rect(),
- 0xff,
- media::VIDEO_ROTATION_0);
+ video_renderer.Copy(video_frame.get(), &canvas);
CopyFromCompositingSurfaceCallback(quit_callback,
result,
const std::vector<gfx::Rect>& character_bounds) OVERRIDE;
virtual void RenderProcessGone(base::TerminationStatus status,
int error_code) OVERRIDE;
+ virtual void RenderWidgetHostGone() OVERRIDE;
virtual void Destroy() OVERRIDE;
virtual void SetTooltipText(const base::string16& tooltip_text) OVERRIDE;
virtual void SelectionChanged(const base::string16& text,
Destroy();
}
+void RenderWidgetHostViewMac::RenderWidgetHostGone() {
+ // Destroy the DelegatedFrameHost, to prevent crashes when Destroy is never
+ // called on the view.
+ // http://crbug.com/404828
+ ShutdownBrowserCompositor();
+}
+
void RenderWidgetHostViewMac::Destroy() {
[[NSNotificationCenter defaultCenter]
removeObserver:cocoa_view_
gfx::Image image;
if (entry && entry->screenshot().get()) {
std::vector<gfx::ImagePNGRep> image_reps;
- image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(),
- ui::GetScaleFactorForNativeView(window_.get())));
+ image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(), 1.0f));
image = gfx::Image(image_reps);
}
if (!layer_delegate_)
#include "base/logging.h"
#include "content/browser/android/interstitial_page_delegate_android.h"
#include "content/browser/frame_host/interstitial_page_impl.h"
+#include "content/browser/media/android/browser_media_player_manager.h"
#include "content/browser/media/media_web_contents_observer.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
web_contents_->WasShown();
}
+void WebContentsAndroid::ReleaseMediaPlayers(JNIEnv* env, jobject jobj) {
+#if defined(ENABLE_BROWSER_CDMS)
+ RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(
+ web_contents_->GetRenderViewHost());
+ if (!rvhi || !rvhi->GetMainFrame())
+ return;
+
+ BrowserMediaPlayerManager* manager =
+ rvhi->media_web_contents_observer()->GetMediaPlayerManager(
+ rvhi->GetMainFrame());
+ if (manager)
+ manager->ReleaseAllMediaPlayers();
+#endif // defined(ENABLE_BROWSER_CDMS)
+}
+
void WebContentsAndroid::PauseVideo() {
RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(
web_contents_->GetRenderViewHost());
void OnHide(JNIEnv* env, jobject obj);
void OnShow(JNIEnv* env, jobject obj);
+ void ReleaseMediaPlayers(JNIEnv* env, jobject jobj);
void PauseVideo();
+
void AddStyleSheetByURL(
JNIEnv* env, jobject obj, jstring url);
void ShowInterstitialPage(
gfx::Image image;
if (entry && entry->screenshot().get()) {
std::vector<gfx::ImagePNGRep> image_reps;
- image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(),
- ui::GetScaleFactorForNativeView(web_contents_window())));
+ image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(), 1.0f));
image = gfx::Image(image_reps);
}
SetImage(image);
case kGinJavaBridgeNonAssignableTypes:
return "The type of the object passed to the method is incompatible "
"with the type of method's argument";
+ case kGinJavaBridgeRenderFrameDeleted:
+ return "RenderFrame has been deleted";
}
NOTREACHED();
return "Unknown error";
kGinJavaBridgeAccessToObjectGetClassIsBlocked,
kGinJavaBridgeJavaExceptionRaised,
kGinJavaBridgeNonAssignableTypes,
+ kGinJavaBridgeRenderFrameDeleted,
};
CONTENT_EXPORT const char* GinJavaBridgeErrorToString(GinJavaBridgeError error);
}
void GpuVideoDecodeAcceleratorHost::OnPictureReady(
- int32 picture_buffer_id, int32 bitstream_buffer_id) {
+ int32 picture_buffer_id,
+ int32 bitstream_buffer_id,
+ const gfx::Rect& visible_rect) {
DCHECK(CalledOnValidThread());
if (!client_)
return;
- media::Picture picture(picture_buffer_id, bitstream_buffer_id);
+ media::Picture picture(picture_buffer_id, bitstream_buffer_id, visible_rect);
client_->PictureReady(picture);
}
const gfx::Size& dimensions,
uint32 texture_target);
void OnDismissPictureBuffer(int32 picture_buffer_id);
- void OnPictureReady(int32 picture_buffer_id, int32 bitstream_buffer_id);
+ void OnPictureReady(int32 picture_buffer_id,
+ int32 bitstream_buffer_id,
+ const gfx::Rect& visible_rect);
void OnFlushDone();
void OnResetDone();
void OnNotifyError(uint32 error);
int32) /* Picture buffer ID */
// Decoder reports that a picture is ready.
-IPC_MESSAGE_ROUTED2(AcceleratedVideoDecoderHostMsg_PictureReady,
- int32, /* Picture buffer ID */
- int32) /* Bitstream buffer ID */
+IPC_MESSAGE_ROUTED3(AcceleratedVideoDecoderHostMsg_PictureReady,
+ int32, /* Picture buffer ID */
+ int32, /* Bitstream buffer ID */
+ gfx::Rect) /* Visible rectangle */
// Confirm decoder has been flushed.
IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderHostMsg_FlushDone)
base::MessageLoop::current()->PostTask(
FROM_HERE,
- base::Bind(&AndroidVideoDecodeAccelerator::NotifyPictureReady,
- weak_this_factory_.GetWeakPtr(),
- media::Picture(picture_buffer_id, bitstream_id)));
+ base::Bind(
+ &AndroidVideoDecodeAccelerator::NotifyPictureReady,
+ weak_this_factory_.GetWeakPtr(),
+ media::Picture(picture_buffer_id, bitstream_id, gfx::Rect(size_))));
}
void AndroidVideoDecodeAccelerator::Decode(
RETURN_AND_NOTIFY_ON_FAILURE((state_ != kUninitialized),
"Invalid state: " << state_, ILLEGAL_STATE,);
- if (output_picture_buffers_.empty())
+ if (output_picture_buffers_.empty() && stale_output_picture_buffers_.empty())
return;
OutputBuffers::iterator it = output_picture_buffers_.find(picture_buffer_id);
- RETURN_AND_NOTIFY_ON_FAILURE(it != output_picture_buffers_.end(),
- "Invalid picture id: " << picture_buffer_id, INVALID_ARGUMENT,);
+ // If we didn't find the picture id in the |output_picture_buffers_| map we
+ // try the |stale_output_picture_buffers_| map, as this may have been an
+ // output picture buffer from before a resolution change, that at resolution
+ // change time had yet to be displayed. The client is calling us back to tell
+ // us that we can now recycle this picture buffer, so if we were waiting to
+ // dispose of it we now can.
+ if (it == output_picture_buffers_.end()) {
+ it = stale_output_picture_buffers_.find(picture_buffer_id);
+ RETURN_AND_NOTIFY_ON_FAILURE(it != stale_output_picture_buffers_.end(),
+ "Invalid picture id: " << picture_buffer_id, INVALID_ARGUMENT,);
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&DXVAVideoDecodeAccelerator::DeferredDismissStaleBuffer,
+ weak_this_factory_.GetWeakPtr(), picture_buffer_id));
+ return;
+ }
it->second->ReusePictureBuffer();
ProcessPendingSamples();
PLATFORM_FAILURE, );
media::Picture output_picture(index->second->id(),
- sample_info.input_buffer_id);
+ sample_info.input_buffer_id,
+ gfx::Rect(index->second->size()));
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&DXVAVideoDecodeAccelerator::NotifyPictureReady,
return;
weak_this_factory_.InvalidateWeakPtrs();
output_picture_buffers_.clear();
+ stale_output_picture_buffers_.clear();
pending_output_samples_.clear();
pending_input_buffers_.clear();
decoder_.Release();
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&DXVAVideoDecodeAccelerator::DismissStaleBuffers,
- weak_this_factory_.GetWeakPtr(),
- output_picture_buffers_));
+ weak_this_factory_.GetWeakPtr()));
base::MessageLoop::current()->PostTask(
FROM_HERE,
weak_this_factory_.GetWeakPtr(),
width,
height));
-
- output_picture_buffers_.clear();
}
-void DXVAVideoDecodeAccelerator::DismissStaleBuffers(
- const OutputBuffers& picture_buffers) {
- OutputBuffers::const_iterator index;
+void DXVAVideoDecodeAccelerator::DismissStaleBuffers() {
+ OutputBuffers::iterator index;
- for (index = picture_buffers.begin();
- index != picture_buffers.end();
+ for (index = output_picture_buffers_.begin();
+ index != output_picture_buffers_.end();
++index) {
- DVLOG(1) << "Dismissing picture id: " << index->second->id();
- client_->DismissPictureBuffer(index->second->id());
+ if (index->second->available()) {
+ DVLOG(1) << "Dismissing picture id: " << index->second->id();
+ client_->DismissPictureBuffer(index->second->id());
+ } else {
+ // Move to |stale_output_picture_buffers_| for deferred deletion.
+ stale_output_picture_buffers_.insert(
+ std::make_pair(index->first, index->second));
+ }
}
+
+ output_picture_buffers_.clear();
+}
+
+void DXVAVideoDecodeAccelerator::DeferredDismissStaleBuffer(
+ int32 picture_buffer_id) {
+ OutputBuffers::iterator it = stale_output_picture_buffers_.find(
+ picture_buffer_id);
+ DCHECK(it != stale_output_picture_buffers_.end());
+ DVLOG(1) << "Dismissing picture id: " << it->second->id();
+ client_->DismissPictureBuffer(it->second->id());
+ stale_output_picture_buffers_.erase(it);
}
} // namespace content
typedef std::map<int32, linked_ptr<DXVAPictureBuffer> > OutputBuffers;
// Tells the client to dismiss the stale picture buffers passed in.
- void DismissStaleBuffers(const OutputBuffers& picture_buffers);
+ void DismissStaleBuffers();
+
+ // Called after the client indicates we can recycle a stale picture buffer.
+ void DeferredDismissStaleBuffer(int32 picture_buffer_id);
// To expose client callbacks from VideoDecodeAccelerator.
media::VideoDecodeAccelerator::Client* client_;
// The key is the picture buffer id.
OutputBuffers output_picture_buffers_;
+ // After a resolution change there may be a few output buffers which have yet
+ // to be displayed so they cannot be dismissed immediately. We move them from
+ // |output_picture_buffers_| to this map so they may be dismissed once they
+ // become available.
+ OutputBuffers stale_output_picture_buffers_;
+
// Set to true if we requested picture slots from the client.
bool pictures_requested_;
if (!Send(new AcceleratedVideoDecoderHostMsg_PictureReady(
host_route_id_,
picture.picture_buffer_id(),
- picture.bitstream_buffer_id()))) {
+ picture.bitstream_buffer_id(),
+ picture.visible_rect()))) {
DLOG(ERROR) << "Send(AcceleratedVideoDecoderHostMsg_PictureReady) failed";
}
}
#include <vector>
#include "base/bind.h"
+#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/message_loop/message_loop.h"
RenderingHelperParams::~RenderingHelperParams() {}
+VideoFrameTexture::VideoFrameTexture(uint32 texture_target,
+ uint32 texture_id,
+ const base::Closure& no_longer_needed_cb)
+ : texture_target_(texture_target),
+ texture_id_(texture_id),
+ no_longer_needed_cb_(no_longer_needed_cb) {
+ DCHECK(!no_longer_needed_cb_.is_null());
+}
+
+VideoFrameTexture::~VideoFrameTexture() {
+ base::ResetAndReturn(&no_longer_needed_cb_).Run();
+}
+
+RenderingHelper::RenderedVideo::RenderedVideo() : last_frame_rendered(false) {
+}
+
+RenderingHelper::RenderedVideo::~RenderedVideo() {
+}
+
// static
bool RenderingHelper::InitializeOneOff() {
base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
}
RenderingHelper::~RenderingHelper() {
- CHECK_EQ(clients_.size(), 0U) << "Must call UnInitialize before dtor.";
+ CHECK_EQ(videos_.size(), 0U) << "Must call UnInitialize before dtor.";
Clear();
}
void RenderingHelper::Initialize(const RenderingHelperParams& params,
base::WaitableEvent* done) {
- // Use cients_.size() != 0 as a proxy for the class having already been
+ // Use videos_.size() != 0 as a proxy for the class having already been
// Initialize()'d, and UnInitialize() before continuing.
- if (clients_.size()) {
+ if (videos_.size()) {
base::WaitableEvent done(false, false);
UnInitialize(&done);
done.Wait();
NULL, gl_surface_, gfx::PreferIntegratedGpu);
gl_context_->MakeCurrent(gl_surface_);
- clients_ = params.clients;
- CHECK_GT(clients_.size(), 0U);
- LayoutRenderingAreas();
+ CHECK_GT(params.window_sizes.size(), 0U);
+ videos_.resize(params.window_sizes.size());
+ LayoutRenderingAreas(params.window_sizes);
if (render_as_thumbnails_) {
- CHECK_EQ(clients_.size(), 1U);
+ CHECK_EQ(videos_.size(), 1U);
GLint max_texture_size;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
++frame_count_;
}
+void RenderingHelper::QueueVideoFrame(
+ size_t window_id,
+ scoped_refptr<VideoFrameTexture> video_frame) {
+ CHECK_EQ(base::MessageLoop::current(), message_loop_);
+ RenderedVideo* video = &videos_[window_id];
+
+ // Pop the last frame if it has been rendered.
+ if (video->last_frame_rendered) {
+ // When last_frame_rendered is true, we should have only one pending frame.
+ // Since we are going to have a new frame, we can release the pending one.
+ DCHECK(video->pending_frames.size() == 1);
+ video->pending_frames.pop();
+ video->last_frame_rendered = false;
+ }
+
+ video->pending_frames.push(video_frame);
+}
+
+void RenderingHelper::DropPendingFrames(size_t window_id) {
+ CHECK_EQ(base::MessageLoop::current(), message_loop_);
+ RenderedVideo* video = &videos_[window_id];
+ video->pending_frames = std::queue<scoped_refptr<VideoFrameTexture> >();
+ video->last_frame_rendered = false;
+}
+
void RenderingHelper::RenderTexture(uint32 texture_target, uint32 texture_id) {
// The ExternalOES sampler is bound to GL_TEXTURE1 and the Texture2D sampler
// is bound to GL_TEXTURE0.
}
void RenderingHelper::DeleteTexture(uint32 texture_id) {
+ CHECK_EQ(base::MessageLoop::current(), message_loop_);
glDeleteTextures(1, &texture_id);
CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR);
}
}
void RenderingHelper::Clear() {
- clients_.clear();
+ videos_.clear();
message_loop_ = NULL;
gl_context_ = NULL;
gl_surface_ = NULL;
CHECK_EQ(base::MessageLoop::current(), message_loop_);
glUniform1i(glGetUniformLocation(program_, "tex_flip"), 1);
+ // Frames that will be returned to the client (via the no_longer_needed_cb)
+ // after this vector falls out of scope at the end of this method. We need
+ // to keep references to them until after SwapBuffers() call below.
+ std::vector<scoped_refptr<VideoFrameTexture> > frames_to_be_returned;
+
if (render_as_thumbnails_) {
// In render_as_thumbnails_ mode, we render the FBO content on the
// screen instead of the decoded textures.
- GLSetViewPort(render_areas_[0]);
+ GLSetViewPort(videos_[0].render_area);
RenderTexture(GL_TEXTURE_2D, thumbnails_texture_id_);
} else {
- for (size_t i = 0; i < clients_.size(); ++i) {
- if (clients_[i]) {
- GLSetViewPort(render_areas_[i]);
- clients_[i]->RenderContent(this);
+ for (size_t i = 0; i < videos_.size(); ++i) {
+ RenderedVideo* video = &videos_[i];
+ if (video->pending_frames.empty())
+ continue;
+ scoped_refptr<VideoFrameTexture> frame = video->pending_frames.front();
+ GLSetViewPort(video->render_area);
+ RenderTexture(frame->texture_target(), frame->texture_id());
+
+ if (video->pending_frames.size() > 1) {
+ frames_to_be_returned.push_back(video->pending_frames.front());
+ video->pending_frames.pop();
+ } else {
+ video->last_frame_rendered = true;
}
}
}
}
}
-void RenderingHelper::LayoutRenderingAreas() {
+void RenderingHelper::LayoutRenderingAreas(
+ const std::vector<gfx::Size>& window_sizes) {
// Find the number of colums and rows.
- // The smallest n * n or n * (n + 1) > number of clients.
- size_t cols = sqrt(clients_.size() - 1) + 1;
- size_t rows = (clients_.size() + cols - 1) / cols;
+ // The smallest n * n or n * (n + 1) > number of windows.
+ size_t cols = sqrt(videos_.size() - 1) + 1;
+ size_t rows = (videos_.size() + cols - 1) / cols;
// Find the widths and heights of the grid.
std::vector<int> widths(cols);
std::vector<int> offset_x(cols);
std::vector<int> offset_y(rows);
- for (size_t i = 0; i < clients_.size(); ++i) {
- const gfx::Size& window_size = clients_[i]->GetWindowSize();
- widths[i % cols] = std::max(widths[i % cols], window_size.width());
- heights[i / cols] = std::max(heights[i / cols], window_size.height());
+ for (size_t i = 0; i < window_sizes.size(); ++i) {
+ const gfx::Size& size = window_sizes[i];
+ widths[i % cols] = std::max(widths[i % cols], size.width());
+ heights[i / cols] = std::max(heights[i / cols], size.height());
}
ScaleAndCalculateOffsets(&widths, &offset_x, screen_size_.width());
ScaleAndCalculateOffsets(&heights, &offset_y, screen_size_.height());
// Put each render_area_ in the center of each cell.
- render_areas_.clear();
- for (size_t i = 0; i < clients_.size(); ++i) {
- const gfx::Size& window_size = clients_[i]->GetWindowSize();
+ for (size_t i = 0; i < window_sizes.size(); ++i) {
+ const gfx::Size& size = window_sizes[i];
float scale =
- std::min(static_cast<float>(widths[i % cols]) / window_size.width(),
- static_cast<float>(heights[i / cols]) / window_size.height());
+ std::min(static_cast<float>(widths[i % cols]) / size.width(),
+ static_cast<float>(heights[i / cols]) / size.height());
// Don't scale up the texture.
scale = std::min(1.0f, scale);
- size_t w = scale * window_size.width();
- size_t h = scale * window_size.height();
+ size_t w = scale * size.width();
+ size_t h = scale * size.height();
size_t x = offset_x[i % cols] + (widths[i % cols] - w) / 2;
size_t y = offset_y[i / cols] + (heights[i / cols] - h) / 2;
- render_areas_.push_back(gfx::Rect(x, y, w, h));
+ videos_[i].render_area = gfx::Rect(x, y, w, h);
}
}
} // namespace content
#define CONTENT_COMMON_GPU_MEDIA_RENDERING_HELPER_H_
#include <map>
+#include <queue>
#include <vector>
#include "base/basictypes.h"
namespace content {
-struct RenderingHelperParams;
+class VideoFrameTexture : public base::RefCounted<VideoFrameTexture> {
+ public:
+ uint32 texture_id() const { return texture_id_; }
+ uint32 texture_target() const { return texture_target_; }
+
+ VideoFrameTexture(uint32 texture_target,
+ uint32 texture_id,
+ const base::Closure& no_longer_needed_cb);
+
+ private:
+ friend class base::RefCounted<VideoFrameTexture>;
+
+ uint32 texture_target_;
+ uint32 texture_id_;
+ base::Closure no_longer_needed_cb_;
+
+ ~VideoFrameTexture();
+};
+
+struct RenderingHelperParams {
+ RenderingHelperParams();
+ ~RenderingHelperParams();
+
+ // The rendering FPS.
+ int rendering_fps;
+
+ // The desired size of each window. We play each stream in its own window
+ // on the screen.
+ std::vector<gfx::Size> window_sizes;
+
+ // The members below are only used for the thumbnail mode where all frames
+ // are rendered in sequence onto one FBO for comparison/verification purposes.
+
+ // Whether the frames are rendered as scaled thumbnails within a
+ // larger FBO that is in turn rendered to the window.
+ bool render_as_thumbnails;
+ // The size of the FBO containing all visible thumbnails.
+ gfx::Size thumbnails_page_size;
+ // The size of each thumbnail within the FBO.
+ gfx::Size thumbnail_size;
+};
// Creates and draws textures used by the video decoder.
// This class is not thread safe and thus all the methods of this class
// (except for ctor/dtor) ensure they're being run on a single thread.
class RenderingHelper {
public:
- // Interface for the content provider of the RenderingHelper.
- class Client {
- public:
- // Callback to tell client to render the content.
- virtual void RenderContent(RenderingHelper* helper) = 0;
-
- // Callback to get the desired window size of the client.
- virtual const gfx::Size& GetWindowSize() = 0;
-
- protected:
- virtual ~Client() {}
- };
RenderingHelper();
~RenderingHelper();
// |texture_target|.
void RenderThumbnail(uint32 texture_target, uint32 texture_id);
- // Render |texture_id| to the current view port of the screen using target
- // |texture_target|.
- void RenderTexture(uint32 texture_target, uint32 texture_id);
+ // Queues the |video_frame| for rendering.
+ void QueueVideoFrame(size_t window_id,
+ scoped_refptr<VideoFrameTexture> video_frame);
+
+ // Drops all the pending video frames of the specified window.
+ void DropPendingFrames(size_t window_id);
// Delete |texture_id|.
void DeleteTexture(uint32 texture_id);
base::WaitableEvent* done);
private:
+ struct RenderedVideo {
+ // The rect on the screen where the video will be rendered.
+ gfx::Rect render_area;
+
+ // True if the last (and the only one) frame in pending_frames has
+ // been rendered. We keep the last remaining frame in pending_frames even
+ // after it has been rendered, so that we have something to display if the
+ // client is falling behind on providing us with new frames during
+ // timer-driven playback.
+ bool last_frame_rendered;
+
+ // The video frames pending for rendering.
+ std::queue<scoped_refptr<VideoFrameTexture> > pending_frames;
+
+ RenderedVideo();
+ ~RenderedVideo();
+ };
+
void Clear();
void RenderContent();
- void LayoutRenderingAreas();
+ void LayoutRenderingAreas(const std::vector<gfx::Size>& window_sizes);
+
+ // Render |texture_id| to the current view port of the screen using target
+ // |texture_target|.
+ void RenderTexture(uint32 texture_target, uint32 texture_id);
// Timer to trigger the RenderContent() repeatly.
scoped_ptr<base::RepeatingTimer<RenderingHelper> > render_timer_;
gfx::Size screen_size_;
- // The rendering area of each window on the screen.
- std::vector<gfx::Rect> render_areas_;
-
- std::vector<base::WeakPtr<Client> > clients_;
+ std::vector<RenderedVideo> videos_;
bool render_as_thumbnails_;
int frame_count_;
DISALLOW_COPY_AND_ASSIGN(RenderingHelper);
};
-struct RenderingHelperParams {
- RenderingHelperParams();
- ~RenderingHelperParams();
-
- // The rendering FPS.
- int rendering_fps;
-
- // The clients who provide the content for rendering.
- std::vector<base::WeakPtr<RenderingHelper::Client> > clients;
-
- // Whether the frames are rendered as scaled thumbnails within a
- // larger FBO that is in turn rendered to the window.
- bool render_as_thumbnails;
- // The size of the FBO containing all visible thumbnails.
- gfx::Size thumbnails_page_size;
- // The size of each thumbnail within the FBO.
- gfx::Size thumbnail_size;
-};
} // namespace content
#endif // CONTENT_COMMON_GPU_MEDIA_RENDERING_HELPER_H_
DVLOG(3) << "Dequeue(): returning input_id=" << dqbuf.timestamp.tv_sec
<< " as picture_id=" << output_record.picture_id;
const media::Picture& picture =
- media::Picture(output_record.picture_id, dqbuf.timestamp.tv_sec);
+ media::Picture(output_record.picture_id,
+ dqbuf.timestamp.tv_sec,
+ gfx::Rect(frame_buffer_size_));
pending_picture_ready_.push(
PictureRecord(output_record.cleared, picture));
SendPictureReady();
TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_);
DVLOG(4) << "Notifying output picture id " << output_id
<< " for input "<< input_id << " is ready";
+ // TODO(posciak): Use visible size from decoder here instead
+ // (crbug.com/402760).
if (client_)
- client_->PictureReady(media::Picture(output_id, input_id));
+ client_->PictureReady(
+ media::Picture(output_id, input_id, gfx::Rect(tfp_picture->size())));
}
void VaapiVideoDecodeAccelerator::TryOutputSurface() {
// the TESTs below.
class GLRenderingVDAClient
: public VideoDecodeAccelerator::Client,
- public RenderingHelper::Client,
public base::SupportsWeakPtr<GLRenderingVDAClient> {
public:
+ // |window_id| the window_id of the client, which is used to identify the
+ // rendering area in the |rendering_helper|.
// Doesn't take ownership of |rendering_helper| or |note|, which must outlive
// |*this|.
// |num_play_throughs| indicates how many times to play through the video.
// will start delaying the call to ReusePictureBuffer() for kReuseDelay.
// |decode_calls_per_second| is the number of VDA::Decode calls per second.
// If |decode_calls_per_second| > 0, |num_in_flight_decodes| must be 1.
- GLRenderingVDAClient(RenderingHelper* rendering_helper,
+ GLRenderingVDAClient(size_t window_id,
+ RenderingHelper* rendering_helper,
ClientStateNotification<ClientState>* note,
const std::string& encoded_data,
int num_in_flight_decodes,
virtual void NotifyResetDone() OVERRIDE;
virtual void NotifyError(VideoDecodeAccelerator::Error error) OVERRIDE;
- // RenderingHelper::Client implementation.
- virtual void RenderContent(RenderingHelper*) OVERRIDE;
- virtual const gfx::Size& GetWindowSize() OVERRIDE;
-
void OutputFrameDeliveryTimes(base::File* output);
- void NotifyFrameDropped(int32 picture_buffer_id);
-
// Simple getters for inspecting the state of the Client.
int num_done_bitstream_buffers() { return num_done_bitstream_buffers_; }
int num_skipped_fragments() { return num_skipped_fragments_; }
// Request decode of the next fragment in the encoded data.
void DecodeNextFragment();
+ size_t window_id_;
RenderingHelper* rendering_helper_;
gfx::Size frame_size_;
std::string encoded_data_;
// The number of VDA::Decode calls per second. This is to simulate webrtc.
int decode_calls_per_second_;
bool render_as_thumbnails_;
- bool pending_picture_updated_;
- std::deque<int32> pending_picture_buffer_ids_;
DISALLOW_IMPLICIT_CONSTRUCTORS(GLRenderingVDAClient);
};
GLRenderingVDAClient::GLRenderingVDAClient(
+ size_t window_id,
RenderingHelper* rendering_helper,
ClientStateNotification<ClientState>* note,
const std::string& encoded_data,
int delay_reuse_after_frame_num,
int decode_calls_per_second,
bool render_as_thumbnails)
- : rendering_helper_(rendering_helper),
+ : window_id_(window_id),
+ rendering_helper_(rendering_helper),
frame_size_(frame_width, frame_height),
encoded_data_(encoded_data),
num_in_flight_decodes_(num_in_flight_decodes),
suppress_rendering_(suppress_rendering),
delay_reuse_after_frame_num_(delay_reuse_after_frame_num),
decode_calls_per_second_(decode_calls_per_second),
- render_as_thumbnails_(render_as_thumbnails),
- pending_picture_updated_(true) {
+ render_as_thumbnails_(render_as_thumbnails) {
CHECK_GT(num_in_flight_decodes, 0);
CHECK_GT(num_play_throughs, 0);
// |num_in_flight_decodes_| is unsupported if |decode_calls_per_second_| > 0.
picture_buffers_by_id_.erase(it);
}
-void GLRenderingVDAClient::RenderContent(RenderingHelper*) {
- CHECK(!render_as_thumbnails_);
-
- // No decoded texture for rendering yet, just skip.
- if (pending_picture_buffer_ids_.size() == 0)
- return;
-
- int32 buffer_id = pending_picture_buffer_ids_.front();
- media::PictureBuffer* picture_buffer = picture_buffers_by_id_[buffer_id];
-
- CHECK(picture_buffer);
- if (!pending_picture_updated_) {
- // Frame dropped, just redraw the last texture.
- rendering_helper_->RenderTexture(texture_target_,
- picture_buffer->texture_id());
- return;
- }
-
- base::TimeTicks now = base::TimeTicks::Now();
- frame_delivery_times_.push_back(now);
-
- rendering_helper_->RenderTexture(texture_target_,
- picture_buffer->texture_id());
-
- if (pending_picture_buffer_ids_.size() == 1) {
- pending_picture_updated_ = false;
- } else {
- pending_picture_buffer_ids_.pop_front();
- ReturnPicture(buffer_id);
- }
-}
-
-const gfx::Size& GLRenderingVDAClient::GetWindowSize() {
- return render_as_thumbnails_ ? kThumbnailsPageSize : frame_size_;
-}
-
void GLRenderingVDAClient::PictureReady(const media::Picture& picture) {
// We shouldn't be getting pictures delivered after Reset has completed.
CHECK_LT(state_, CS_RESET);
return;
base::TimeTicks now = base::TimeTicks::Now();
+
+ frame_delivery_times_.push_back(now);
+
// Save the decode time of this picture.
std::map<int, base::TimeTicks>::iterator it =
decode_start_time_.find(picture.bitstream_buffer_id());
encoded_data_next_pos_to_decode_ = 0;
}
+ media::PictureBuffer* picture_buffer =
+ picture_buffers_by_id_[picture.picture_buffer_id()];
+ CHECK(picture_buffer);
+
+ scoped_refptr<VideoFrameTexture> video_frame =
+ new VideoFrameTexture(texture_target_,
+ picture_buffer->texture_id(),
+ base::Bind(&GLRenderingVDAClient::ReturnPicture,
+ AsWeakPtr(),
+ picture.picture_buffer_id()));
+
if (render_as_thumbnails_) {
- frame_delivery_times_.push_back(now);
- media::PictureBuffer* picture_buffer =
- picture_buffers_by_id_[picture.picture_buffer_id()];
- CHECK(picture_buffer);
- rendering_helper_->RenderThumbnail(texture_target_,
- picture_buffer->texture_id());
- ReturnPicture(picture.picture_buffer_id());
+ rendering_helper_->RenderThumbnail(video_frame->texture_target(),
+ video_frame->texture_id());
} else if (!suppress_rendering_) {
- // Keep the picture for rendering.
- pending_picture_buffer_ids_.push_back(picture.picture_buffer_id());
- if (pending_picture_buffer_ids_.size() > 1 && !pending_picture_updated_) {
- ReturnPicture(pending_picture_buffer_ids_.front());
- pending_picture_buffer_ids_.pop_front();
- pending_picture_updated_ = true;
- }
- } else {
- frame_delivery_times_.push_back(now);
- ReturnPicture(picture.picture_buffer_id());
+ rendering_helper_->QueueVideoFrame(window_id_, video_frame);
}
}
if (decoder_deleted())
return;
- // Clear pending_pictures and reuse them.
- while (!pending_picture_buffer_ids_.empty()) {
- decoder_->ReusePictureBuffer(pending_picture_buffer_ids_.front());
- pending_picture_buffer_ids_.pop_front();
- }
- pending_picture_updated_ = true;
+ rendering_helper_->DropPendingFrames(window_id_);
if (reset_after_frame_num_ == MID_STREAM_RESET) {
reset_after_frame_num_ = END_OF_STREAM_RESET;
}
}
-void GLRenderingVDAClient::NotifyFrameDropped(int32 picture_buffer_id) {
- decoder_->ReusePictureBuffer(picture_buffer_id);
-}
-
static bool LookingAtNAL(const std::string& encoded, size_t pos) {
return encoded[pos] == 0 && encoded[pos + 1] == 0 &&
encoded[pos + 2] == 0 && encoded[pos + 3] == 1;
}
GLRenderingVDAClient* client =
- new GLRenderingVDAClient(&rendering_helper_,
+ new GLRenderingVDAClient(index,
+ &rendering_helper_,
note,
video_file->data_str,
num_in_flight_decodes,
render_as_thumbnails);
clients[index] = client;
- helper_params.clients.push_back(client->AsWeakPtr());
+ helper_params.window_sizes.push_back(
+ render_as_thumbnails
+ ? kThumbnailsPageSize
+ : gfx::Size(video_file->width, video_file->height));
}
InitializeRenderingHelper(helper_params);
ClientStateNotification<ClientState>* note =
new ClientStateNotification<ClientState>();
GLRenderingVDAClient* client =
- new GLRenderingVDAClient(&rendering_helper_,
+ new GLRenderingVDAClient(0,
+ &rendering_helper_,
note,
test_video_files_[0]->data_str,
1,
std::numeric_limits<int>::max(),
kWebRtcDecodeCallsPerSecond,
false /* render_as_thumbnail */);
- helper_params.clients.push_back(client->AsWeakPtr());
+ helper_params.window_sizes.push_back(
+ gfx::Size(test_video_files_[0]->width, test_video_files_[0]->height));
InitializeRenderingHelper(helper_params);
CreateAndStartDecoder(client, note);
WaitUntilDecodeFinish(note);
0)); // plane
picture_bindings_[picture_id] = frame.image_buffer;
- client_->PictureReady(media::Picture(picture_id, frame.bitstream_id));
+ client_->PictureReady(media::Picture(
+ picture_id, frame.bitstream_id, gfx::Rect(texture_size_)));
client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id);
}
// Accessibility touch exploration state.
private boolean mTouchExplorationEnabled;
+ // Whether accessibility focus should be set to the page when it finishes loading.
+ // This only applies if an accessibility service like TalkBack is running.
+ // This is desirable behavior for a browser window, but not for an embedded
+ // WebView.
+ private boolean mShouldSetAccessibilityFocusOnPageLoad;
+
// Allows us to dynamically respond when the accessibility script injection flag changes.
private ContentObserver mAccessibilityScriptInjectionObserver;
@SuppressWarnings("deprecation") // AbsoluteLayout
public void setAnchorViewPosition(
View view, float x, float y, float width, float height) {
+ if (view.getParent() == null) {
+ // Ignore. setAnchorViewPosition has been called after the anchor view has
+ // already been released.
+ return;
+ }
assert view.getParent() == mContainerViewAtCreation;
float scale = (float) DeviceDisplayInfo.create(mContext).getDIPScale();
@SuppressWarnings("unused")
@CalledByNative
- private void showPastePopup(int xDip, int yDip) {
- if (!mHasInsertion || !canPaste()) return;
+ private void showPastePopupWithFeedback(int xDip, int yDip) {
+ // TODO(jdduke): Remove this when there is a better signal that long press caused
+ // showing of the paste popup. See http://crbug.com/150151.
+ if (showPastePopup(xDip, yDip)) {
+ mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ }
+ }
+
+ private boolean showPastePopup(int xDip, int yDip) {
+ if (!mHasInsertion || !canPaste()) return false;
final float contentOffsetYPix = mRenderCoordinates.getContentOffsetYPix();
getPastePopup().showAt(
(int) mRenderCoordinates.fromDipToPix(xDip),
(int) (mRenderCoordinates.fromDipToPix(yDip) + contentOffsetYPix));
+ return true;
}
private PastePopupMenu getPastePopup() {
}
/**
+ * Return whether or not we should set accessibility focus on page load.
+ */
+ public boolean shouldSetAccessibilityFocusOnPageLoad() {
+ return mShouldSetAccessibilityFocusOnPageLoad;
+ }
+
+ /**
+ * Return whether or not we should set accessibility focus on page load.
+ * This only applies if an accessibility service like TalkBack is running.
+ * This is desirable behavior for a browser window, but not for an embedded
+ * WebView.
+ */
+ public void setShouldSetAccessibilityFocusOnPageLoad(boolean on) {
+ mShouldSetAccessibilityFocusOnPageLoad = on;
+ }
+
+ /**
* Inform WebKit that Fullscreen mode has been exited by the user.
*/
public void exitFullscreen() {
}
/**
+ * Trigger a redraw of the compositor. This is only needed if the UI changes something that
+ * does not trigger a redraw itself by updating the layer tree.
+ */
+ public void setNeedsComposite() {
+ if (mNativeContentViewRenderView == 0) return;
+ nativeSetNeedsComposite(mNativeContentViewRenderView);
+ }
+
+ /**
* This method should be subclassed to provide actions to be performed once the view is ready to
* render.
*/
}
}
+ /**
+ * @return Native pointer for the UI resource provider taken from the compositor.
+ */
+ public long getUIResourceProvider() {
+ return nativeGetUIResourceProvider(mNativeContentViewRenderView);
+ }
+
private native long nativeInit(long rootWindowNativePointer);
+ private native long nativeGetUIResourceProvider(long nativeContentViewRenderView);
private native void nativeDestroy(long nativeContentViewRenderView);
private native void nativeSetCurrentContentViewCore(long nativeContentViewRenderView,
long nativeContentViewCore);
int format, int width, int height, Surface surface);
private native void nativeSetOverlayVideoMode(long nativeContentViewRenderView,
boolean enabled);
+ private native void nativeSetNeedsComposite(long nativeContentViewRenderView);
}
@Override
protected Void doInBackground(Void... unused) {
- if (!mOutputDir.exists() && !mOutputDir.mkdirs()) {
+ final File outputDir = getOutputDir();
+ if (!outputDir.exists() && !outputDir.mkdirs()) {
Log.e(LOGTAG, "Unable to create pak resources directory!");
return null;
}
- String timestampFile = checkPakTimestamp();
+ String timestampFile = checkPakTimestamp(outputDir);
if (timestampFile != null) {
- deleteFiles(mContext);
+ deleteFiles();
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
&& filenames.size() >= sMandatoryPaks.length) {
boolean filesPresent = true;
for (String file : filenames) {
- if (!new File(mOutputDir, file).exists()) {
+ if (!new File(outputDir, file).exists()) {
filesPresent = false;
break;
}
continue;
}
boolean isICUData = file.equals(ICU_DATA_FILENAME);
- File output = new File(isICUData ? mAppDataDir : mOutputDir, file);
+ File output = new File(isICUData ? getAppDataDir() : outputDir, file);
if (output.exists()) {
continue;
}
// returning null? It might be useful to gather UMA here too to track if
// this happens with regularity.
Log.w(LOGTAG, "Exception unpacking required pak resources: " + e.getMessage());
- deleteFiles(mContext);
+ deleteFiles();
return null;
}
if (timestampFile != null) {
try {
- new File(mOutputDir, timestampFile).createNewFile();
+ new File(outputDir, timestampFile).createNewFile();
} catch (IOException e) {
// Worst case we don't write a timestamp, so we'll re-extract the resource
// paks next start up.
// Note that we do this to avoid adding a BroadcastReceiver on
// android.content.Intent#ACTION_PACKAGE_CHANGED as that causes process churn
// on (re)installation of *all* APK files.
- private String checkPakTimestamp() {
+ private String checkPakTimestamp(File outputDir) {
final String TIMESTAMP_PREFIX = "pak_timestamp-";
PackageManager pm = mContext.getPackageManager();
PackageInfo pi = null;
String expectedTimestamp = TIMESTAMP_PREFIX + pi.versionCode + "-" + pi.lastUpdateTime;
- String[] timestamps = mOutputDir.list(new FilenameFilter() {
+ String[] timestamps = outputDir.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.startsWith(TIMESTAMP_PREFIX);
private final Context mContext;
private ExtractTask mExtractTask;
- private final File mAppDataDir;
- private final File mOutputDir;
private static ResourceExtractor sInstance;
}
private ResourceExtractor(Context context) {
- mContext = context;
- mAppDataDir = getAppDataDirFromContext(mContext);
- mOutputDir = getOutputDirFromContext(mContext);
+ mContext = context.getApplicationContext();
}
public void waitForCompletion() {
sInstance = null;
} catch (CancellationException e) {
// Don't leave the files in an inconsistent state.
- deleteFiles(mContext);
+ deleteFiles();
} catch (ExecutionException e2) {
- deleteFiles(mContext);
+ deleteFiles();
} catch (InterruptedException e3) {
- deleteFiles(mContext);
+ deleteFiles();
}
}
mExtractTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
- public static File getAppDataDirFromContext(Context context) {
- return new File(PathUtils.getDataDirectory(context.getApplicationContext()));
+ private File getAppDataDir() {
+ return new File(PathUtils.getDataDirectory(mContext));
}
- public static File getOutputDirFromContext(Context context) {
- return new File(getAppDataDirFromContext(context), "paks");
+ private File getOutputDir() {
+ return new File(getAppDataDir(), "paks");
}
/**
* lead to malfunction/UX misbehavior. So, we regard failing to update them
* as an error.
*/
- public static void deleteFiles(Context context) {
- File icudata = new File(getAppDataDirFromContext(context), ICU_DATA_FILENAME);
+ private void deleteFiles() {
+ File icudata = new File(getAppDataDir(), ICU_DATA_FILENAME);
if (icudata.exists() && !icudata.delete()) {
Log.e(LOGTAG, "Unable to remove the icudata " + icudata.getName());
}
- File dir = getOutputDirFromContext(context);
+ File dir = getOutputDir();
if (dir.exists()) {
File[] files = dir.listFiles();
for (File file : files) {
private void handlePageLoaded(int id) {
if (mUserHasTouchExplored) return;
- mAccessibilityFocusId = id;
- sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ if (mContentViewCore.shouldSetAccessibilityFocusOnPageLoad()) {
+ mAccessibilityFocusId = id;
+ sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ }
}
@CalledByNative
}
@Override
+ public void releaseMediaPlayers() {
+ nativeReleaseMediaPlayers(mNativeWebContentsAndroid);
+ }
+
+ @Override
public int getBackgroundColor() {
return nativeGetBackgroundColor(mNativeWebContentsAndroid);
}
private native void nativeInsertCSS(long nativeWebContentsAndroid, String css);
private native void nativeOnHide(long nativeWebContentsAndroid);
private native void nativeOnShow(long nativeWebContentsAndroid);
+ private native void nativeReleaseMediaPlayers(long nativeWebContentsAndroid);
private native int nativeGetBackgroundColor(long nativeWebContentsAndroid);
private native void nativeAddStyleSheetByURL(long nativeWebContentsAndroid,
String url);
public void onShow();
/**
+ * Stops all media players for this WebContents.
+ */
+ public void releaseMediaPlayers();
+
+ /**
* Get the Background color from underlying RenderWidgetHost for this WebContent.
*/
public int getBackgroundColor();
@SmallTest
@Feature({"TextInput", "Main"})
+ public void testKeyCodesWhileSwipingText() throws Throwable {
+ DOMUtils.focusNode(mContentViewCore, "textarea");
+ assertWaitForKeyboardStatus(true);
+
+ // The calls below are a reflection of what the stock Google Keyboard (Android 4.4) sends
+ // when the word is swiped on the soft keyboard. Exercise care when altering to make sure
+ // that the test reflects reality. If this test breaks, it's possible that code has
+ // changed and different calls need to be made instead.
+ mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
+ waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
+
+ // "three"
+ expectUpdateStateCall(mConnection);
+ setComposingText(mConnection, "three", 1);
+ assertEquals(KeyEvent.KEYCODE_UNKNOWN, mImeAdapter.mLastSyntheticKeyCode);
+ assertUpdateStateCall(mConnection, 1000);
+ assertEquals("three", mConnection.getTextBeforeCursor(99, 0));
+
+ // "word"
+ commitText(mConnection, "three", 1);
+ commitText(mConnection, " ", 1);
+ expectUpdateStateCall(mConnection);
+ setComposingText(mConnection, "word", 1);
+ assertEquals(KeyEvent.KEYCODE_UNKNOWN, mImeAdapter.mLastSyntheticKeyCode);
+ assertUpdateStateCall(mConnection, 1000);
+ assertEquals("three word", mConnection.getTextBeforeCursor(99, 0));
+
+ // "test"
+ commitText(mConnection, "word", 1);
+ commitText(mConnection, " ", 1);
+ expectUpdateStateCall(mConnection);
+ setComposingText(mConnection, "test", 1);
+ assertEquals(KeyEvent.KEYCODE_UNKNOWN, mImeAdapter.mLastSyntheticKeyCode);
+ assertUpdateStateCall(mConnection, 1000);
+ assertEquals("three word test", mConnection.getTextBeforeCursor(99, 0));
+ }
+
+ @SmallTest
+ @Feature({"TextInput", "Main"})
public void testKeyCodesWhileTypingText() throws Throwable {
DOMUtils.focusNode(mContentViewCore, "textarea");
assertWaitForKeyboardStatus(true);
- // The calls below are a reflection of what the Hacker's Keyboard sends when the noted
+ // The calls below are a reflection of what the Hacker's Keyboard sends when the noted
// key is touched on screen. Exercise care when altering to make sure that the test
// reflects reality.
mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
// Enables VP8 HW encode acceleration for WebRTC.
const char kEnableWebRtcHWVp8Encoding[] = "enable-webrtc-hw-vp8-encoding";
+
+// Enables H264 HW encode acceleration for WebRTC.
+const char kEnableWebRtcHWH264Encoding[] = "enable-webrtc-hw-h264-encoding";
#endif
#if defined(OS_ANDROID)
CONTENT_EXPORT extern const char kDisableWebRtcEncryption[];
CONTENT_EXPORT extern const char kDisableWebRtcHWEncoding[];
CONTENT_EXPORT extern const char kEnableWebRtcHWVp8Encoding[];
+CONTENT_EXPORT extern const char kEnableWebRtcHWH264Encoding[];
#endif
#if defined(OS_ANDROID)
if (!params.link_url.is_empty()) {
blink::WebNode selectedNode = DomUtils::ExtractParentAnchorNode(data.node);
blink::WebElement selectedElement = selectedNode.to<blink::WebElement>();
- if (selectedNode.isLink() && !selectedElement.isNull()) {
+ if (!selectedElement.isNull() && selectedNode.isLink()) {
params.link_text = selectedElement.innerText();
} else {
LOG(ERROR) << "Creating a ContextMenuParams for a node that has a link"
void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas,
const blink::WebRect& rect,
unsigned char alpha) {
+ paint(canvas, rect, alpha, SkXfermode::kSrcOver_Mode);
+}
+
+void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas,
+ const blink::WebRect& rect,
+ unsigned char alpha,
+ SkXfermode::Mode mode) {
DCHECK(main_thread_checker_.CalledOnValidThread());
scoped_ptr<blink::WebGraphicsContext3DProvider> provider =
scoped_ptr<blink::WebGraphicsContext3DProvider>(blink::Platform::current(
dest.set(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
SkPaint paint;
paint.setAlpha(alpha);
+ paint.setXfermodeMode(mode);
// It is not necessary to pass the dest into the drawBitmap call since all
// the context have been set up before calling paintCurrentFrameInContext.
canvas->drawBitmapRect(bitmap_, 0, dest, &paint);
// https://code.google.com/p/skia/issues/detail?id=1189
virtual void paint(blink::WebCanvas* canvas,
const blink::WebRect& rect,
+ unsigned char alpha,
+ SkXfermode::Mode mode);
+ // TODO(dshwang): remove it because above method replaces. crbug.com/401027
+ virtual void paint(blink::WebCanvas* canvas,
+ const blink::WebRect& rect,
unsigned char alpha);
virtual bool copyVideoTextureToPlatformTexture(
#include "ipc/ipc_logging.h"
#include "ipc/ipc_sender.h"
-namespace content {
-
namespace {
+
const int kStreamIDNotSet = -1;
+
+void LogMessage(int stream_id, const std::string& msg) {
+ std::ostringstream oss;
+ oss << "[stream_id=" << stream_id << "] AIMF::" << msg;
+ content::WebRtcLogMessage(oss.str());
+ DVLOG(1) << oss.str();
}
+}
+
+namespace content {
+
class AudioInputMessageFilter::AudioInputIPCImpl
: public NON_EXPORTED_BASE(media::AudioInputIPC) {
public:
uint32 length,
uint32 total_segments) {
DCHECK(io_message_loop_->BelongsToCurrentThread());
-
- WebRtcLogMessage(base::StringPrintf(
- "AIMF::OnStreamCreated. stream_id=%d",
- stream_id));
+ LogMessage(stream_id, "OnStreamCreated");
#if !defined(OS_WIN)
base::SyncSocket::Handle socket_handle = socket_descriptor.fd;
DCHECK(delegate);
stream_id_ = filter_->delegates_.Add(delegate);
+ // TODO(henrika): remove all LogMessage calls when we have sorted out the
+ // existing "no input audio" issues.
+ LogMessage(stream_id_, "CreateStream");
AudioInputHostMsg_CreateStream_Config config;
config.params = params;
void AudioInputMessageFilter::AudioInputIPCImpl::RecordStream() {
DCHECK_NE(stream_id_, kStreamIDNotSet);
+ LogMessage(stream_id_, "RecordStream");
filter_->Send(new AudioInputHostMsg_RecordStream(stream_id_));
}
void AudioInputMessageFilter::AudioInputIPCImpl::CloseStream() {
DCHECK(filter_->io_message_loop_->BelongsToCurrentThread());
DCHECK_NE(stream_id_, kStreamIDNotSet);
+ LogMessage(stream_id_, "CloseStream");
filter_->Send(new AudioInputHostMsg_CloseStream(stream_id_));
filter_->delegates_.Remove(stream_id_);
stream_id_ = kStreamIDNotSet;
const int MediaStreamVideoSource::kDefaultWidth = 640;
const int MediaStreamVideoSource::kDefaultHeight = 480;
const int MediaStreamVideoSource::kDefaultFrameRate = 30;
+const int MediaStreamVideoSource::kUnknownFrameRate = 0;
namespace {
static const int kDefaultWidth;
static const int kDefaultHeight;
static const int kDefaultFrameRate;
+ static const int kUnknownFrameRate;
protected:
virtual void DoStopSource() OVERRIDE;
RTCVideoDecoder::BufferData::BufferData(int32 bitstream_buffer_id,
uint32_t timestamp,
- int width,
- int height,
size_t size)
: bitstream_buffer_id(bitstream_buffer_id),
timestamp(timestamp),
- width(width),
- height(height),
size(size) {}
RTCVideoDecoder::BufferData::BufferData() {}
RTCVideoDecoder::BufferData::~BufferData() {}
RTCVideoDecoder::RTCVideoDecoder(
+ webrtc::VideoCodecType type,
const scoped_refptr<media::GpuVideoAcceleratorFactories>& factories)
- : factories_(factories),
+ : video_codec_type_(type),
+ factories_(factories),
decoder_texture_target_(0),
next_picture_buffer_id_(0),
state_(UNINITIALIZED),
case webrtc::kVideoCodecVP8:
profile = media::VP8PROFILE_ANY;
break;
+ case webrtc::kVideoCodecH264:
+ profile = media::H264PROFILE_MAIN;
+ break;
default:
DVLOG(2) << "Video codec not supported:" << type;
return decoder.Pass();
}
base::WaitableEvent waiter(true, false);
- decoder.reset(new RTCVideoDecoder(factories));
+ decoder.reset(new RTCVideoDecoder(type, factories));
decoder->factories_->GetTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&RTCVideoDecoder::CreateVDA,
profile,
&waiter));
waiter.Wait();
- // vda can be NULL if VP8 is not supported.
+ // vda can be NULL if the codec is not supported.
if (decoder->vda_ != NULL) {
decoder->state_ = INITIALIZED;
} else {
int32_t RTCVideoDecoder::InitDecode(const webrtc::VideoCodec* codecSettings,
int32_t /*numberOfCores*/) {
DVLOG(2) << "InitDecode";
- DCHECK_EQ(codecSettings->codecType, webrtc::kVideoCodecVP8);
- if (codecSettings->codecSpecific.VP8.feedbackModeOn) {
+ DCHECK_EQ(video_codec_type_, codecSettings->codecType);
+ if (codecSettings->codecType == webrtc::kVideoCodecVP8 &&
+ codecSettings->codecSpecific.VP8.feedbackModeOn) {
LOG(ERROR) << "Feedback mode not supported";
return RecordInitDecodeUMA(WEBRTC_VIDEO_CODEC_ERROR);
}
// Create buffer metadata.
BufferData buffer_data(next_bitstream_buffer_id_,
inputImage._timeStamp,
- frame_size_.width(),
- frame_size_.height(),
inputImage._length);
// Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & ID_LAST;
}
const media::PictureBuffer& pb = it->second;
+ // Validate picture rectangle from GPU.
+ if (picture.visible_rect().IsEmpty() ||
+ !gfx::Rect(pb.size()).Contains(picture.visible_rect())) {
+ NOTREACHED() << "Invalid picture size from VDA: "
+ << picture.visible_rect().ToString() << " should fit in "
+ << pb.size().ToString();
+ NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
+ return;
+ }
+
// Create a media::VideoFrame.
- uint32_t timestamp = 0, width = 0, height = 0;
- size_t size = 0;
- GetBufferData(
- picture.bitstream_buffer_id(), ×tamp, &width, &height, &size);
+ uint32_t timestamp = 0;
+ GetBufferData(picture.bitstream_buffer_id(), ×tamp);
scoped_refptr<media::VideoFrame> frame =
- CreateVideoFrame(picture, pb, timestamp, width, height, size);
+ CreateVideoFrame(picture, pb, timestamp);
bool inserted =
picture_buffers_at_display_.insert(std::make_pair(
picture.picture_buffer_id(),
// Create a WebRTC video frame.
webrtc::RefCountImpl<NativeHandleImpl>* handle =
new webrtc::RefCountImpl<NativeHandleImpl>(frame);
- webrtc::TextureVideoFrame decoded_image(handle, width, height, timestamp, 0);
+ webrtc::TextureVideoFrame decoded_image(handle,
+ picture.visible_rect().width(),
+ picture.visible_rect().height(),
+ timestamp,
+ 0);
// Invoke decode callback. WebRTC expects no callback after Reset or Release.
{
scoped_refptr<media::VideoFrame> RTCVideoDecoder::CreateVideoFrame(
const media::Picture& picture,
const media::PictureBuffer& pb,
- uint32_t timestamp,
- uint32_t width,
- uint32_t height,
- size_t size) {
- gfx::Rect visible_rect(width, height);
+ uint32_t timestamp) {
+ gfx::Rect visible_rect(picture.visible_rect());
DCHECK(decoder_texture_target_);
// Convert timestamp from 90KHz to ms.
base::TimeDelta timestamp_ms = base::TimeDelta::FromInternalValue(
}
void RTCVideoDecoder::GetBufferData(int32 bitstream_buffer_id,
- uint32_t* timestamp,
- uint32_t* width,
- uint32_t* height,
- size_t* size) {
+ uint32_t* timestamp) {
for (std::list<BufferData>::iterator it = input_buffer_data_.begin();
it != input_buffer_data_.end();
++it) {
if (it->bitstream_buffer_id != bitstream_buffer_id)
continue;
*timestamp = it->timestamp;
- *width = it->width;
- *height = it->height;
return;
}
NOTREACHED() << "Missing bitstream buffer id: " << bitstream_buffer_id;
struct BufferData {
BufferData(int32 bitstream_buffer_id,
uint32_t timestamp,
- int width,
- int height,
size_t size);
BufferData();
~BufferData();
int32 bitstream_buffer_id;
uint32_t timestamp; // in 90KHz
- uint32_t width;
- uint32_t height;
size_t size; // buffer size
};
FRIEND_TEST_ALL_PREFIXES(RTCVideoDecoderTest, IsFirstBufferAfterReset);
RTCVideoDecoder(
+ webrtc::VideoCodecType type,
const scoped_refptr<media::GpuVideoAcceleratorFactories>& factories);
// Requests a buffer to be decoded by VDA.
scoped_refptr<media::VideoFrame> CreateVideoFrame(
const media::Picture& picture,
const media::PictureBuffer& pb,
- uint32_t timestamp,
- uint32_t width,
- uint32_t height,
- size_t size);
+ uint32_t timestamp);
// Resets VDA.
void ResetInternal();
// Stores the buffer metadata to |input_buffer_data_|.
void RecordBufferData(const BufferData& buffer_data);
// Gets the buffer metadata from |input_buffer_data_|.
- void GetBufferData(int32 bitstream_buffer_id,
- uint32_t* timestamp,
- uint32_t* width,
- uint32_t* height,
- size_t* size);
+ void GetBufferData(int32 bitstream_buffer_id, uint32_t* timestamp);
// Records the result of InitDecode to UMA and returns |status|.
int32_t RecordInitDecodeUMA(int32_t status);
// The hardware video decoder.
scoped_ptr<media::VideoDecodeAccelerator> vda_;
+ // The video codec type, as reported by WebRTC.
+ const webrtc::VideoCodecType video_codec_type_;
+
// The size of the incoming video frames.
gfx::Size frame_size_;
.Times(1)
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_vda_, Destroy()).Times(1);
- rtc_decoder_ =
- RTCVideoDecoder::Create(webrtc::kVideoCodecVP8, mock_gpu_factories_);
}
virtual void TearDown() OVERRIDE {
return WEBRTC_VIDEO_CODEC_OK;
}
+ void CreateDecoder(webrtc::VideoCodecType codec_type) {
+ VLOG(2) << "CreateDecoder";
+ codec_.codecType = codec_type;
+ rtc_decoder_ =
+ RTCVideoDecoder::Create(codec_type, mock_gpu_factories_);
+ }
+
void Initialize() {
VLOG(2) << "Initialize";
- codec_.codecType = webrtc::kVideoCodecVP8;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->InitDecode(&codec_, 1));
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
rtc_decoder_->RegisterDecodeCompleteCallback(this));
};
TEST_F(RTCVideoDecoderTest, CreateReturnsNullOnUnsupportedCodec) {
+ CreateDecoder(webrtc::kVideoCodecVP8);
scoped_ptr<RTCVideoDecoder> null_rtc_decoder(
RTCVideoDecoder::Create(webrtc::kVideoCodecI420, mock_gpu_factories_));
EXPECT_EQ(NULL, null_rtc_decoder.get());
}
+TEST_F(RTCVideoDecoderTest, CreateAndInitSucceedsForH264Codec) {
+ CreateDecoder(webrtc::kVideoCodecH264);
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->InitDecode(&codec_, 1));
+}
+
TEST_F(RTCVideoDecoderTest, InitDecodeReturnsErrorOnFeedbackMode) {
- codec_.codecType = webrtc::kVideoCodecVP8;
+ CreateDecoder(webrtc::kVideoCodecVP8);
codec_.codecSpecific.VP8.feedbackModeOn = true;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, rtc_decoder_->InitDecode(&codec_, 1));
}
TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorWithoutInitDecode) {
+ CreateDecoder(webrtc::kVideoCodecVP8);
webrtc::EncodedImage input_image;
EXPECT_EQ(WEBRTC_VIDEO_CODEC_UNINITIALIZED,
rtc_decoder_->Decode(input_image, false, NULL, NULL, 0));
}
TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorOnIncompleteFrame) {
+ CreateDecoder(webrtc::kVideoCodecVP8);
Initialize();
webrtc::EncodedImage input_image;
input_image._completeFrame = false;
}
TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorOnMissingFrames) {
+ CreateDecoder(webrtc::kVideoCodecVP8);
Initialize();
webrtc::EncodedImage input_image;
input_image._completeFrame = true;
}
TEST_F(RTCVideoDecoderTest, ResetReturnsOk) {
+ CreateDecoder(webrtc::kVideoCodecVP8);
Initialize();
EXPECT_CALL(*mock_vda_, Reset())
.WillOnce(Invoke(this, &RTCVideoDecoderTest::NotifyResetDone));
}
TEST_F(RTCVideoDecoderTest, ReleaseReturnsOk) {
+ CreateDecoder(webrtc::kVideoCodecVP8);
Initialize();
EXPECT_CALL(*mock_vda_, Reset())
.WillOnce(Invoke(this, &RTCVideoDecoderTest::NotifyResetDone));
}
TEST_F(RTCVideoDecoderTest, InitDecodeAfterRelease) {
+ CreateDecoder(webrtc::kVideoCodecVP8);
EXPECT_CALL(*mock_vda_, Reset())
.WillRepeatedly(Invoke(this, &RTCVideoDecoderTest::NotifyResetDone));
Initialize();
}
TEST_F(RTCVideoDecoderTest, IsBufferAfterReset) {
+ CreateDecoder(webrtc::kVideoCodecVP8);
EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(0, RTCVideoDecoder::ID_INVALID));
EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_LAST,
RTCVideoDecoder::ID_INVALID));
}
TEST_F(RTCVideoDecoderTest, IsFirstBufferAfterReset) {
+ CreateDecoder(webrtc::kVideoCodecVP8);
EXPECT_TRUE(
rtc_decoder_->IsFirstBufferAfterReset(0, RTCVideoDecoder::ID_INVALID));
EXPECT_FALSE(
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
#include "media/filters/gpu_video_accelerator_factories.h"
+#include "media/filters/h264_parser.h"
#include "media/video/video_encode_accelerator.h"
#include "third_party/webrtc/system_wrappers/interface/tick_util.h"
namespace content {
+namespace {
+
+// Populates struct webrtc::RTPFragmentationHeader for H264 codec.
+// Each entry specifies the offset and length (excluding start code) of a NALU.
+// Returns true if successful.
+bool GetRTPFragmentationHeaderH264(webrtc::RTPFragmentationHeader* header,
+ const uint8_t* data, uint32_t length) {
+ media::H264Parser parser;
+ parser.SetStream(data, length);
+
+ std::vector<media::H264NALU> nalu_vector;
+ while (true) {
+ media::H264NALU nalu;
+ const media::H264Parser::Result result = parser.AdvanceToNextNALU(&nalu);
+ if (result == media::H264Parser::kOk) {
+ nalu_vector.push_back(nalu);
+ } else if (result == media::H264Parser::kEOStream) {
+ break;
+ } else {
+ DLOG(ERROR) << "Unexpected H264 parser result";
+ return false;
+ }
+ }
+
+ header->VerifyAndAllocateFragmentationHeader(nalu_vector.size());
+ for (size_t i = 0; i < nalu_vector.size(); ++i) {
+ header->fragmentationOffset[i] = nalu_vector[i].data - data;
+ header->fragmentationLength[i] = nalu_vector[i].size;
+ header->fragmentationPlType[i] = 0;
+ header->fragmentationTimeDiff[i] = 0;
+ }
+ return true;
+}
+
+} // namespace
+
// This private class of RTCVideoEncoder does the actual work of communicating
// with a media::VideoEncodeAccelerator for handling video encoding. It can
// be created on any thread, but should subsequently be posted to (and Destroy()
if (!encoded_image_callback_)
return;
+ webrtc::RTPFragmentationHeader header;
+ memset(&header, 0, sizeof(header));
+ switch (video_codec_type_) {
+ case webrtc::kVideoCodecVP8:
+ case webrtc::kVideoCodecGeneric:
+ // Generate a header describing a single fragment.
+ // Note that webrtc treats the generic-type payload as an opaque buffer.
+ header.VerifyAndAllocateFragmentationHeader(1);
+ header.fragmentationOffset[0] = 0;
+ header.fragmentationLength[0] = image->_length;
+ header.fragmentationPlType[0] = 0;
+ header.fragmentationTimeDiff[0] = 0;
+ break;
+ case webrtc::kVideoCodecH264:
+ if (!GetRTPFragmentationHeaderH264(
+ &header, image->_buffer, image->_length)) {
+ DLOG(ERROR) << "Failed to get RTP fragmentation header for H264";
+ NotifyError(WEBRTC_VIDEO_CODEC_ERROR);
+ return;
+ }
+ break;
+ default:
+ NOTREACHED() << "Invalid video codec type";
+ return;
+ }
+
webrtc::CodecSpecificInfo info;
memset(&info, 0, sizeof(info));
info.codecType = video_codec_type_;
info.codecSpecific.VP8.keyIdx = -1;
}
- // Generate a header describing a single fragment.
- webrtc::RTPFragmentationHeader header;
- memset(&header, 0, sizeof(header));
- header.VerifyAndAllocateFragmentationHeader(1);
- header.fragmentationOffset[0] = 0;
- header.fragmentationLength[0] = image->_length;
- header.fragmentationPlType[0] = 0;
- header.fragmentationTimeDiff[0] = 0;
-
int32_t retval = encoded_image_callback_->Encoded(*image, &info, &header);
if (retval < 0) {
DVLOG(2) << "ReturnEncodedImage(): encoded_image_callback_ returned "
#include "content/renderer/media/rtc_video_encoder_factory.h"
+#include "base/command_line.h"
#include "content/common/gpu/client/gpu_video_encode_accelerator_host.h"
+#include "content/public/common/content_switches.h"
#include "content/renderer/media/rtc_video_encoder.h"
#include "media/filters/gpu_video_accelerator_factories.h"
#include "media/video/video_encode_accelerator.h"
namespace {
// Translate from media::VideoEncodeAccelerator::SupportedProfile to
-// cricket::WebRtcVideoEncoderFactory::VideoCodec
-cricket::WebRtcVideoEncoderFactory::VideoCodec VEAToWebRTCCodec(
+// one or more instances of cricket::WebRtcVideoEncoderFactory::VideoCodec
+void VEAToWebRTCCodecs(
+ std::vector<cricket::WebRtcVideoEncoderFactory::VideoCodec>* codecs,
const media::VideoEncodeAccelerator::SupportedProfile& profile) {
- webrtc::VideoCodecType type = webrtc::kVideoCodecUnknown;
- std::string name;
- int width = 0, height = 0, fps = 0;
+ int width = profile.max_resolution.width();
+ int height = profile.max_resolution.height();
+ int fps = profile.max_framerate.numerator;
+ DCHECK_EQ(profile.max_framerate.denominator, 1U);
+ const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
if (profile.profile >= media::VP8PROFILE_MIN &&
profile.profile <= media::VP8PROFILE_MAX) {
- type = webrtc::kVideoCodecVP8;
- name = "VP8";
+ if (cmd_line->HasSwitch(switches::kEnableWebRtcHWVp8Encoding)) {
+ codecs->push_back(cricket::WebRtcVideoEncoderFactory::VideoCodec(
+ webrtc::kVideoCodecVP8, "VP8", width, height, fps));
+ }
} else if (profile.profile >= media::H264PROFILE_MIN &&
profile.profile <= media::H264PROFILE_MAX) {
- type = webrtc::kVideoCodecGeneric;
- name = "CAST1";
- }
-
- if (type != webrtc::kVideoCodecUnknown) {
- width = profile.max_resolution.width();
- height = profile.max_resolution.height();
- fps = profile.max_framerate.numerator;
- DCHECK_EQ(profile.max_framerate.denominator, 1U);
+ if (cmd_line->HasSwitch(switches::kEnableWebRtcHWH264Encoding)) {
+ codecs->push_back(cricket::WebRtcVideoEncoderFactory::VideoCodec(
+ webrtc::kVideoCodecH264, "H264", width, height, fps));
+ }
+ // TODO(hshi): remove the generic codec type after CASTv1 deprecation.
+ codecs->push_back(cricket::WebRtcVideoEncoderFactory::VideoCodec(
+ webrtc::kVideoCodecGeneric, "CAST1", width, height, fps));
}
-
- return cricket::WebRtcVideoEncoderFactory::VideoCodec(
- type, name, width, height, fps);
}
// Translate from cricket::WebRtcVideoEncoderFactory::VideoCodec to
switch (type) {
case webrtc::kVideoCodecVP8:
return media::VP8PROFILE_ANY;
+ case webrtc::kVideoCodecH264:
case webrtc::kVideoCodecGeneric:
return media::H264PROFILE_MAIN;
default:
// Query media::VideoEncodeAccelerator (statically) for our supported codecs.
std::vector<media::VideoEncodeAccelerator::SupportedProfile> profiles =
GpuVideoEncodeAcceleratorHost::GetSupportedProfiles();
- for (size_t i = 0; i < profiles.size(); ++i) {
- VideoCodec codec = VEAToWebRTCCodec(profiles[i]);
- if (codec.type != webrtc::kVideoCodecUnknown)
- codecs_.push_back(codec);
- }
+ for (size_t i = 0; i < profiles.size(); ++i)
+ VEAToWebRTCCodecs(&codecs_, profiles[i]);
}
RTCVideoEncoderFactory::~RTCVideoEncoderFactory() {}
const float kFirstFrameTimeoutInFrameIntervals = 100.0f;
const float kNormalFrameTimeoutInFrameIntervals = 25.0f;
+// Min delta time between two frames allowed without being dropped if a max
+// frame rate is specified.
+const int kMinTimeInMsBetweenFrames = 5;
+
// Empty method used for keeping a reference to the original media::VideoFrame
// in VideoFrameResolutionAdapter::DeliverFrame if cropping is needed.
// The reference to |frame| is kept in the closure that calls this method.
// Returns |true| if the input frame rate is higher that the requested max
// frame rate and |frame| should be dropped.
- bool MaybeDropFrame(const scoped_refptr<media::VideoFrame>& frame);
+ bool MaybeDropFrame(const scoped_refptr<media::VideoFrame>& frame,
+ float source_frame_rate);
// Bound to the IO-thread.
base::ThreadChecker io_thread_checker_;
const base::TimeTicks& estimated_capture_time) {
DCHECK(io_thread_checker_.CalledOnValidThread());
- if (MaybeDropFrame(frame))
+ if (MaybeDropFrame(frame, format.frame_rate))
return;
// TODO(perkj): Allow cropping / scaling of textures once
}
bool VideoTrackAdapter::VideoFrameResolutionAdapter::MaybeDropFrame(
- const scoped_refptr<media::VideoFrame>& frame) {
- if (max_frame_rate_ == 0.0f)
+ const scoped_refptr<media::VideoFrame>& frame,
+ float source_frame_rate) {
+ DCHECK(io_thread_checker_.CalledOnValidThread());
+
+ // Do not drop frames if max frame rate hasn't been specified or the source
+ // frame rate is known and is lower than max.
+ if (max_frame_rate_ == 0.0f ||
+ (source_frame_rate > 0 &&
+ source_frame_rate <= max_frame_rate_))
return false;
base::TimeDelta delta = frame->timestamp() - last_time_stamp_;
+ if (delta.InMilliseconds() < kMinTimeInMsBetweenFrames) {
+ // We have seen video frames being delivered from camera devices back to
+ // back. The simple AR filter for frame rate calculation is too short to
+ // handle that. http://crbug/394315
+ // TODO(perkj): Can we come up with a way to fix the times stamps and the
+ // timing when frames are delivered so all frames can be used?
+ // The time stamps are generated by Chrome and not the actual device.
+ // Most likely the back to back problem is caused by software and not the
+ // actual camera.
+ DVLOG(3) << "Drop frame since delta time since previous frame is "
+ << delta.InMilliseconds() << "ms.";
+ return true;
+ }
last_time_stamp_ = frame->timestamp();
- if (delta.ToInternalValue() == 0 || delta == last_time_stamp_)
+ if (delta == last_time_stamp_) // First received frame.
return false;
- // Calculate the moving average frame rate. Use a simple filter with 0.1
- // weight of the current sample.
+ // Calculate the frame rate using a simple AR filter.
+ // Use a simple filter with 0.1 weight of the current sample.
frame_rate_ = 100 / delta.InMillisecondsF() + 0.9 * frame_rate_;
// Prefer to not drop frames.
return pipeline_progress || data_progress;
}
-void WebMediaPlayerImpl::paint(WebCanvas* canvas,
- const WebRect& rect,
+void WebMediaPlayerImpl::paint(blink::WebCanvas* canvas,
+ const blink::WebRect& rect,
unsigned char alpha) {
+ paint(canvas, rect, alpha, SkXfermode::kSrcOver_Mode);
+}
+
+void WebMediaPlayerImpl::paint(blink::WebCanvas* canvas,
+ const blink::WebRect& rect,
+ unsigned char alpha,
+ SkXfermode::Mode mode) {
DCHECK(main_loop_->BelongsToCurrentThread());
TRACE_EVENT0("media", "WebMediaPlayerImpl:paint");
canvas,
gfx_rect,
alpha,
+ mode,
pipeline_metadata_.video_rotation);
}
// Methods for painting.
virtual void paint(blink::WebCanvas* canvas,
const blink::WebRect& rect,
+ unsigned char alpha,
+ SkXfermode::Mode mode);
+ // TODO(dshwang): remove it because above method replaces. crbug.com/401027
+ virtual void paint(blink::WebCanvas* canvas,
+ const blink::WebRect& rect,
unsigned char alpha);
// True if the loaded media has a playable video/audio track.
return true;
}
-void WebMediaPlayerMS::paint(WebCanvas* canvas,
- const WebRect& rect,
+void WebMediaPlayerMS::paint(blink::WebCanvas* canvas,
+ const blink::WebRect& rect,
unsigned char alpha) {
+ paint(canvas, rect, alpha, SkXfermode::kSrcOver_Mode);
+}
+
+void WebMediaPlayerMS::paint(blink::WebCanvas* canvas,
+ const blink::WebRect& rect,
+ unsigned char alpha,
+ SkXfermode::Mode mode) {
DVLOG(3) << "WebMediaPlayerMS::paint";
DCHECK(thread_checker_.CalledOnValidThread());
gfx::RectF dest_rect(rect.x, rect.y, rect.width, rect.height);
- video_renderer_.Paint(
- current_frame_.get(), canvas, dest_rect, alpha, media::VIDEO_ROTATION_0);
+ video_renderer_.Paint(current_frame_.get(),
+ canvas,
+ dest_rect,
+ alpha,
+ mode,
+ media::VIDEO_ROTATION_0);
{
base::AutoLock auto_lock(current_frame_lock_);
// Methods for painting.
virtual void paint(blink::WebCanvas* canvas,
const blink::WebRect& rect,
+ unsigned char alpha,
+ SkXfermode::Mode mode);
+ // TODO(dshwang): remove it because above method replaces. crbug.com/401027
+ virtual void paint(blink::WebCanvas* canvas,
+ const blink::WebRect& rect,
unsigned char alpha);
// True if the loaded media has a playable video/audio track.
media::VideoCaptureFormat format(
gfx::Size(video_frame->natural_size().width(),
video_frame->natural_size().height()),
- MediaStreamVideoSource::kDefaultFrameRate,
+ MediaStreamVideoSource::kUnknownFrameRate,
pixel_format);
io_message_loop_->PostTask(
gfx::Rect(frame_size), frame_size, timestamp);
media::VideoCaptureFormat format(
frame_size,
- MediaStreamVideoSource::kDefaultFrameRate,
+ MediaStreamVideoSource::kUnknownFrameRate,
media::PIXEL_FORMAT_YV12);
libyuv::BGRAToI420(reinterpret_cast<uint8*>(bitmap->getPixels()),
namespace {
-// Supported hardware sample rates for input and output sides.
-#if defined(OS_WIN) || defined(OS_MACOSX)
-// media::GetAudioInputHardwareSampleRate() asks the audio layer
-// for its current sample rate (set by the user) on Windows and Mac OS X.
-// The listed rates below adds restrictions and WebRtcAudioDeviceImpl::Init()
-// will fail if the user selects any rate outside these ranges.
-const int kValidInputRates[] =
- {192000, 96000, 48000, 44100, 32000, 16000, 8000};
-#elif defined(OS_LINUX) || defined(OS_OPENBSD)
-const int kValidInputRates[] = {48000, 44100};
-#elif defined(OS_ANDROID)
-const int kValidInputRates[] = {48000, 44100};
-#else
-const int kValidInputRates[] = {44100};
-#endif
-
// Time constant for AudioPowerMonitor. See AudioPowerMonitor ctor comments
// for semantics. This value was arbitrarily chosen, but seems to work well.
const int kPowerMonitorTimeConstantMs = 10;
device_info_.device.input.sample_rate);
}
- // Verify that the reported input hardware sample rate is supported
- // on the current platform.
- if (std::find(&kValidInputRates[0],
- &kValidInputRates[0] + arraysize(kValidInputRates),
- device_info_.device.input.sample_rate) ==
- &kValidInputRates[arraysize(kValidInputRates)]) {
- DLOG(ERROR) << device_info_.device.input.sample_rate
- << " is not a supported input rate.";
- return false;
- }
-
// Create and configure the default audio capturing source.
SetCapturerSource(AudioDeviceFactory::NewInputDevice(render_view_id_),
channel_layout,
namespace {
-// Supported hardware sample rates for output sides.
-#if defined(OS_WIN) || defined(OS_MACOSX)
-// AudioHardwareConfig::GetOutputSampleRate() asks the audio layer for its
-// current sample rate (set by the user) on Windows and Mac OS X. The listed
-// rates below adds restrictions and Initialize() will fail if the user selects
-// any rate outside these ranges.
-const int kValidOutputRates[] = {96000, 48000, 44100, 32000, 16000};
-#elif defined(OS_LINUX) || defined(OS_OPENBSD)
-const int kValidOutputRates[] = {48000, 44100};
-#elif defined(OS_ANDROID)
-// TODO(leozwang): We want to use native sampling rate on Android to achieve
-// low latency, currently 16000 is used to work around audio problem on some
-// Android devices.
-const int kValidOutputRates[] = {48000, 44100, 16000};
-#else
-const int kValidOutputRates[] = {44100};
-#endif
-
-// TODO(xians): Merge the following code to WebRtcAudioCapturer, or remove.
-enum AudioFramesPerBuffer {
- k160,
- k320,
- k440,
- k480,
- k640,
- k880,
- k960,
- k1440,
- k1920,
- kUnexpectedAudioBufferSize // Must always be last!
-};
-
-// Helper method to convert integral values to their respective enum values
-// above, or kUnexpectedAudioBufferSize if no match exists.
-// We map 441 to k440 to avoid changes in the XML part for histograms.
-// It is still possible to map the histogram result to the actual buffer size.
-// See http://crbug.com/243450 for details.
-AudioFramesPerBuffer AsAudioFramesPerBuffer(int frames_per_buffer) {
- switch (frames_per_buffer) {
- case 160: return k160;
- case 320: return k320;
- case 441: return k440;
- case 480: return k480;
- case 640: return k640;
- case 880: return k880;
- case 960: return k960;
- case 1440: return k1440;
- case 1920: return k1920;
- }
- return kUnexpectedAudioBufferSize;
-}
-
-void AddHistogramFramesPerBuffer(int param) {
- AudioFramesPerBuffer afpb = AsAudioFramesPerBuffer(param);
- if (afpb != kUnexpectedAudioBufferSize) {
- UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer",
- afpb, kUnexpectedAudioBufferSize);
- } else {
- // Report unexpected sample rates using a unique histogram name.
- UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputFramesPerBufferUnexpected", param);
- }
-}
-
// This is a simple wrapper class that's handed out to users of a shared
// WebRtcAudioRenderer instance. This class maintains the per-user 'playing'
// and 'started' states to avoid problems related to incorrect usage which
return media::AudioParameters::NO_EFFECTS;
}
+// Helper method to get platform specific optimal buffer size.
+int GetOptimalBufferSize(int sample_rate, int hardware_buffer_size) {
+ // Use native hardware buffer size as default. On Windows, we strive to open
+ // up using this native hardware buffer size to achieve best
+ // possible performance and to ensure that no FIFO is needed on the browser
+ // side to match the client request. That is why there is no #if case for
+ // Windows below.
+ int frames_per_buffer = hardware_buffer_size;
+
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+ // On Linux and MacOS, the low level IO implementations on the browser side
+ // supports all buffer size the clients want. We use the native peer
+ // connection buffer size (10ms) to achieve best possible performance.
+ frames_per_buffer = sample_rate / 100;
+#elif defined(OS_ANDROID)
+ // TODO(henrika): Keep tuning this scheme and espcicially for low-latency
+ // cases. Might not be possible to come up with the perfect solution using
+ // the render side only.
+ int frames_per_10ms = sample_rate / 100;
+ if (frames_per_buffer < 2 * frames_per_10ms) {
+ // Examples of low-latency frame sizes and the resulting |buffer_size|:
+ // Nexus 7 : 240 audio frames => 2*480 = 960
+ // Nexus 10 : 256 => 2*441 = 882
+ // Galaxy Nexus: 144 => 2*441 = 882
+ frames_per_buffer = 2 * frames_per_10ms;
+ DVLOG(1) << "Low-latency output detected on Android";
+ }
+#endif
+
+ DVLOG(1) << "Using sink output buffer size: " << frames_per_buffer;
+ return frames_per_buffer;
+}
+
} // namespace
WebRtcAudioRenderer::WebRtcAudioRenderer(
sample_rate);
}
- // Verify that the reported output hardware sample rate is supported
- // on the current platform.
- if (std::find(&kValidOutputRates[0],
- &kValidOutputRates[0] + arraysize(kValidOutputRates),
- sample_rate) ==
- &kValidOutputRates[arraysize(kValidOutputRates)]) {
- DLOG(ERROR) << sample_rate << " is not a supported output rate.";
- return false;
- }
-
// Set up audio parameters for the source, i.e., the WebRTC client.
// The WebRTC client only supports multiples of 10ms as buffer size where
sink_params_.channel_layout(), sink_params_.channels(), 0,
sample_rate, 16, frames_per_10ms);
- // Update audio parameters for the sink, i.e., the native audio output stream.
- // We strive to open up using native parameters to achieve best possible
- // performance and to ensure that no FIFO is needed on the browser side to
- // match the client request. Any mismatch between the source and the sink is
- // taken care of in this class instead using a pull FIFO.
-
- // Use native output size as default.
- int frames_per_buffer = sink_params_.frames_per_buffer();
-#if defined(OS_ANDROID)
- // TODO(henrika): Keep tuning this scheme and espcicially for low-latency
- // cases. Might not be possible to come up with the perfect solution using
- // the render side only.
- if (frames_per_buffer < 2 * frames_per_10ms) {
- // Examples of low-latency frame sizes and the resulting |buffer_size|:
- // Nexus 7 : 240 audio frames => 2*480 = 960
- // Nexus 10 : 256 => 2*441 = 882
- // Galaxy Nexus: 144 => 2*441 = 882
- frames_per_buffer = 2 * frames_per_10ms;
- DVLOG(1) << "Low-latency output detected on Android";
- }
-#endif
- DVLOG(1) << "Using sink output buffer size: " << frames_per_buffer;
+ const int frames_per_buffer =
+ GetOptimalBufferSize(sample_rate, sink_params_.frames_per_buffer());
sink_params_.Reset(sink_params_.format(), sink_params_.channel_layout(),
sink_params_.channels(), 0, sample_rate, 16,
// User must call Play() before any audio can be heard.
state_ = PAUSED;
- UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer",
- source_params.frames_per_buffer(),
- kUnexpectedAudioBufferSize);
- AddHistogramFramesPerBuffer(source_params.frames_per_buffer());
-
return true;
}
// Accessors to the sink audio parameters.
int channels() const { return sink_params_.channels(); }
int sample_rate() const { return sink_params_.sample_rate(); }
+ int frames_per_buffer() const { return sink_params_.frames_per_buffer(); }
private:
// MediaStreamAudioRenderer implementation. This is private since we want
namespace {
+const int kHardwareSampleRate = 44100;
+const int kHardwareBufferSize = 512;
+
class MockAudioOutputIPC : public media::AudioOutputIPC {
public:
MockAudioOutputIPC() {}
factory_(new MockAudioDeviceFactory()),
source_(new MockAudioRendererSource()),
stream_(new rtc::RefCountedObject<MockMediaStream>("label")),
- renderer_(new WebRtcAudioRenderer(stream_, 1, 1, 1, 44100, 441)) {
+ renderer_(new WebRtcAudioRenderer(stream_, 1, 1, 1, 44100,
+ kHardwareBufferSize)) {
EXPECT_CALL(*factory_.get(), CreateOutputDevice(1))
.WillOnce(Return(mock_output_device_));
EXPECT_CALL(*mock_output_device_, Start());
}
}
+// Verify that the sink of the renderer is using the expected sample rate and
+// buffer size.
+TEST_F(WebRtcAudioRendererTest, VerifySinkParameters) {
+ renderer_proxy_->Start();
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+ static const int kExpectedBufferSize = kHardwareSampleRate / 100;
+#elif defined(OS_ANDROID)
+ static const int kExpectedBufferSize = 2 * kHardwareSampleRate / 100;
+#else
+ // Windows.
+ static const int kExpectedBufferSize = kHardwareBufferSize;
+#endif
+ EXPECT_EQ(kExpectedBufferSize, renderer_->frames_per_buffer());
+ EXPECT_EQ(kHardwareSampleRate, renderer_->sample_rate());
+ EXPECT_EQ(2, renderer_->channels());
+
+ EXPECT_CALL(*mock_output_device_.get(), Stop());
+ EXPECT_CALL(*source_.get(), RemoveAudioRenderer(renderer_.get()));
+ renderer_proxy_->Stop();
+}
+
} // namespace content
void ContentDecryptorDelegate::OnPromiseResolved(uint32 promise_id) {
scoped_ptr<CdmPromise> promise = TakePromise(promise_id);
- if (promise) {
- SimpleCdmPromise* simple_promise(
- static_cast<SimpleCdmPromise*>(promise.get()));
- simple_promise->resolve();
+
+ // Special case due to http://crbug.com/408330. CDM is resolving LoadSession()
+ // with this method when the session is not found. Instead it should call
+ // PromiseResolvedWithSession(""), so emulate that here until 408330 is fixed.
+ // TODO(jrummell): Remove this code when the CDM is updated.
+ if (promise &&
+ promise->GetResolveParameterType() == media::CdmPromise::STRING_TYPE) {
+ NewSessionCdmPromise* session_promise =
+ static_cast<NewSessionCdmPromise*>(promise.get());
+ session_promise->resolve(std::string());
+ return;
+ }
+
+ if (!promise ||
+ promise->GetResolveParameterType() != media::CdmPromise::VOID_TYPE) {
+ NOTREACHED();
+ return;
}
+
+ SimpleCdmPromise* simple_promise =
+ static_cast<SimpleCdmPromise*>(promise.get());
+ simple_promise->resolve();
}
void ContentDecryptorDelegate::OnPromiseResolvedWithSession(
uint32 promise_id,
PP_Var web_session_id) {
scoped_ptr<CdmPromise> promise = TakePromise(promise_id);
+ if (!promise ||
+ promise->GetResolveParameterType() != media::CdmPromise::STRING_TYPE) {
+ NOTREACHED();
+ return;
+ }
StringVar* web_session_id_string = StringVar::FromPPVar(web_session_id);
DCHECK(web_session_id_string);
- if (promise) {
- NewSessionCdmPromise* session_promise(
- static_cast<NewSessionCdmPromise*>(promise.get()));
- session_promise->resolve(web_session_id_string->value());
- }
+ NewSessionCdmPromise* session_promise =
+ static_cast<NewSessionCdmPromise*>(promise.get());
+ session_promise->resolve(web_session_id_string->value());
}
void ContentDecryptorDelegate::OnPromiseRejected(
}
void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) {
+ // So far picture.visible_rect is not used. If used, visible_rect should
+ // be validated since it comes from GPU process and may not be trustworthy.
host()->SendUnsolicitedReply(
pp_resource(),
PpapiPluginMsg_VideoDecoder_PictureReady(picture.bitstream_buffer_id(),
}
void PPB_VideoDecoder_Impl::PictureReady(const media::Picture& picture) {
+ // So far picture.visible_rect is not used. If used, visible_rect should
+ // be validated since it comes from GPU process and may not be trustworthy.
DCHECK(RenderThreadImpl::current());
if (!ppp_videodecoder_)
return;
struct VideoDecoderShim::PendingFrame {
explicit PendingFrame(uint32_t decode_id);
- PendingFrame(uint32_t decode_id, const gfx::Size& size);
+ PendingFrame(uint32_t decode_id,
+ const gfx::Size& coded_size,
+ const gfx::Rect& visible_rect);
~PendingFrame();
const uint32_t decode_id;
- const gfx::Size size;
+ const gfx::Size coded_size;
+ const gfx::Rect visible_rect;
std::vector<uint8_t> argb_pixels;
private:
}
VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id,
- const gfx::Size& size)
+ const gfx::Size& coded_size,
+ const gfx::Rect& visible_rect)
: decode_id(decode_id),
- size(size),
- argb_pixels(size.width() * size.height() * 4) {
+ coded_size(coded_size),
+ visible_rect(visible_rect),
+ argb_pixels(coded_size.width() * coded_size.height() * 4) {
}
VideoDecoderShim::PendingFrame::~PendingFrame() {
const scoped_refptr<media::VideoFrame>& frame) {
scoped_ptr<PendingFrame> pending_frame;
if (!frame->end_of_stream()) {
- pending_frame.reset(new PendingFrame(decode_id_, frame->coded_size()));
+ pending_frame.reset(new PendingFrame(
+ decode_id_, frame->coded_size(), frame->visible_rect()));
// Convert the VideoFrame pixels to ABGR to match VideoDecodeAccelerator.
libyuv::I420ToABGR(frame->data(media::VideoFrame::kYPlane),
frame->stride(media::VideoFrame::kYPlane),
DCHECK(host_);
if (!frame->argb_pixels.empty()) {
- if (texture_size_ != frame->size) {
+ if (texture_size_ != frame->coded_size) {
// If the size has changed, all current textures must be dismissed. Add
// all textures to |textures_to_dismiss_| and dismiss any that aren't in
// use by the plugin. We will dismiss the rest as they are recycled.
pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate());
host_->RequestTextures(texture_pool_size_,
- frame->size,
+ frame->coded_size,
GL_TEXTURE_2D,
pending_texture_mailboxes_);
- texture_size_ = frame->size;
+ texture_size_ = frame->coded_size;
}
pending_frames_.push(linked_ptr<PendingFrame>(frame.release()));
GL_UNSIGNED_BYTE,
&frame->argb_pixels.front());
- host_->PictureReady(media::Picture(texture_id, frame->decode_id));
+ host_->PictureReady(
+ media::Picture(texture_id, frame->decode_id, frame->visible_rect));
pending_frames_.pop();
}
base::AutoReset<WebInputEvent::Type> handling_event_type_resetter(
&handling_event_type_, input_event->type);
#if defined(OS_ANDROID)
- // On Android, when the delete key or forward delete key is pressed using IME,
+ // On Android, when a key is pressed or sent from the Keyboard using IME,
// |AdapterInputConnection| generates input key events to make sure all JS
// listeners that monitor KeyUp and KeyDown events receive the proper key
// code. Since this input key event comes from IME, we need to set the
if (WebInputEvent::isKeyboardEventType(input_event->type)) {
const WebKeyboardEvent& key_event =
*static_cast<const WebKeyboardEvent*>(input_event);
- if (key_event.nativeKeyCode == AKEYCODE_FORWARD_DEL ||
- key_event.nativeKeyCode == AKEYCODE_DEL) {
+ // Some keys are special and it's essential that no events get blocked.
+ if (key_event.nativeKeyCode != AKEYCODE_TAB)
ime_event_guard_maybe.reset(new ImeEventGuard(this));
- }
}
#endif
#include <wrl/wrappers/corewrappers.h>
#include "base/debug/alias.h"
+#include "base/debug/crash_logging.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/memory_mapped_file.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
+#include "base/metrics/histogram.h"
#include "base/path_service.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/win/iat_patch_function.h"
#include "base/win/registry.h"
namespace mswr = Microsoft::WRL;
namespace mswrw = Microsoft::WRL::Wrappers;
+static const char kFontKeyName[] = "font_key_name";
+
class FontCollectionLoader
: public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
IDWriteFontCollectionLoader> {
std::wstring GetFontNameFromKey(UINT32 idx);
bool LoadFontListFromRegistry();
+ bool LoadRestrictedFontList();
FontCollectionLoader() {};
virtual ~FontCollectionLoader() {};
UINT64 file_offset,
UINT64 fragment_size,
void** context) {
- if (!memory_.get() || !memory_->IsValid())
+ if (!memory_.get() || !memory_->IsValid() ||
+ file_offset >= memory_->length() ||
+ (file_offset + fragment_size) > memory_->length())
return E_FAIL;
*fragment_start = static_cast<BYTE const*>(memory_->data()) +
HRESULT RuntimeClassInitialize(UINT32 font_key) {
base::FilePath path;
PathService::Get(base::DIR_WINDOWS_FONTS, &path);
- path = path.Append(g_font_loader->GetFontNameFromKey(font_key).c_str());
+ std::wstring font_key_name(g_font_loader->GetFontNameFromKey(font_key));
+ path = path.Append(font_key_name.c_str());
memory_.reset(new base::MemoryMappedFile());
// Put some debug information on stack.
}
font_key_ = font_key;
+
+ base::debug::SetCrashKeyValue(kFontKeyName,
+ base::WideToUTF8(font_key_name));
return S_OK;
}
return false;
}
+ base::FilePath system_font_path;
+ PathService::Get(base::DIR_WINDOWS_FONTS, &system_font_path);
+
std::wstring name;
std::wstring value;
for (DWORD idx = 0; idx < regkey.GetValueCount(); idx++) {
// we will ignore all other registry entries.
std::vector<base::FilePath::StringType> components;
path.GetComponents(&components);
- if (components.size() == 1) {
- reg_fonts_.push_back(value.c_str());
+ if (components.size() == 1 ||
+ base::FilePath::CompareEqualIgnoreCase(system_font_path.value(),
+ path.DirName().value())) {
+ reg_fonts_.push_back(path.BaseName().value());
}
}
}
+ UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Loaded", reg_fonts_.size());
+ UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Ignored",
+ regkey.GetValueCount() - reg_fonts_.size());
+ return true;
+}
+
+// This list is mainly based on prefs/prefs_tab_helper.cc kFontDefaults.
+const wchar_t* kRestrictedFontSet[] = {
+ L"times.ttf", // IDS_STANDARD_FONT_FAMILY
+ L"timesbd.ttf", // IDS_STANDARD_FONT_FAMILY
+ L"timesbi.ttf", // IDS_STANDARD_FONT_FAMILY
+ L"timesi.ttf", // IDS_STANDARD_FONT_FAMILY
+ L"cour.ttf", // IDS_FIXED_FONT_FAMILY
+ L"courbd.ttf", // IDS_FIXED_FONT_FAMILY
+ L"courbi.ttf", // IDS_FIXED_FONT_FAMILY
+ L"couri.ttf", // IDS_FIXED_FONT_FAMILY
+ L"consola.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
+ L"consolab.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
+ L"consolai.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
+ L"consolaz.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
+ L"arial.ttf", // IDS_SANS_SERIF_FONT_FAMILY
+ L"arialbd.ttf", // IDS_SANS_SERIF_FONT_FAMILY
+ L"arialbi.ttf", // IDS_SANS_SERIF_FONT_FAMILY
+ L"ariali.ttf", // IDS_SANS_SERIF_FONT_FAMILY
+ L"comic.ttf", // IDS_CURSIVE_FONT_FAMILY
+ L"comicbd.ttf", // IDS_CURSIVE_FONT_FAMILY
+ L"comici.ttf", // IDS_CURSIVE_FONT_FAMILY
+ L"comicz.ttf", // IDS_CURSIVE_FONT_FAMILY
+ L"impact.ttf", // IDS_FANTASY_FONT_FAMILY
+ L"segoeui.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
+ L"segoeuib.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
+ L"segoeuii.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
+ L"msgothic.ttc", // IDS_STANDARD_FONT_FAMILY_JAPANESE
+ L"msmincho.ttc", // IDS_SERIF_FONT_FAMILY_JAPANESE
+ L"gulim.ttc", // IDS_FIXED_FONT_FAMILY_KOREAN
+ L"batang.ttc", // IDS_SERIF_FONT_FAMILY_KOREAN
+ L"simsun.ttc", // IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN
+ L"mingliu.ttc", // IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN
+};
+
+bool FontCollectionLoader::LoadRestrictedFontList() {
+ reg_fonts_.clear();
+ reg_fonts_.assign(kRestrictedFontSet,
+ kRestrictedFontSet + _countof(kRestrictedFontSet));
return true;
}
FontCollectionLoader::Initialize(factory);
- HRESULT hr = factory->CreateCustomFontCollection(
- g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
+ // We try here to put arbitrary limit on max number of fonts that could
+ // be loaded, otherwise we fallback to restricted set of fonts.
+ const UINT32 kMaxFontThreshold = 1000;
+ HRESULT hr = E_FAIL;
+ if (g_font_loader->GetFontMapSize() < kMaxFontThreshold) {
+ hr = factory->CreateCustomFontCollection(
+ g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
+ }
+
+ bool loadingRestricted = false;
+ if (FAILED(hr) || !g_font_collection.Get()) {
+ // We will try here just one more time with restricted font set.
+ g_font_loader->LoadRestrictedFontList();
+ hr = factory->CreateCustomFontCollection(
+ g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
+ }
base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick;
int64 delta = time_delta.ToInternalValue();
base::debug::Alias(&delta);
UINT32 size = g_font_loader->GetFontMapSize();
base::debug::Alias(&size);
+ base::debug::Alias(&loadingRestricted);
CHECK(SUCCEEDED(hr));
CHECK(g_font_collection.Get() != NULL);
+ UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta);
+
+ base::debug::ClearCrashKey(kFontKeyName);
+
return g_font_collection.Get();
}
bool success) {
LOG_IF(WARNING, !success) << "Failed to connect to " << signal_name
<< "signal.";
+
+ // This is a simple workaround for crbug.com/407109, which causes signals to
+ // be missed if they are received before a match rule is added for them. This
+ // is a branch-only workaround that is only present in 2125 (38).
+ GetAll();
}
object_proxy_,
base::Bind(&PropertyTest::OnPropertyChanged,
base::Unretained(this))));
+
+ // GetAll is called once the signals are connected.
properties_->ConnectSignals();
- properties_->GetAll();
}
virtual void TearDown() {
scoped_refptr<BluetoothSocketChromeOS> socket =
BluetoothSocketChromeOS::CreateBluetoothSocket(
ui_task_runner_, socket_thread_);
- socket->Connect(this, uuid, base::Bind(callback, socket), error_callback);
+ socket->Connect(this, uuid, BluetoothSocketChromeOS::SECURITY_LEVEL_MEDIUM,
+ base::Bind(callback, socket), error_callback);
+}
+
+void BluetoothDeviceChromeOS::ConnectToServiceInsecurely(
+ const BluetoothUUID& uuid,
+ const ConnectToServiceCallback& callback,
+ const ConnectToServiceErrorCallback& error_callback) {
+ VLOG(1) << object_path_.value() << ": Connecting insecurely to service: "
+ << uuid.canonical_value();
+ scoped_refptr<BluetoothSocketChromeOS> socket =
+ BluetoothSocketChromeOS::CreateBluetoothSocket(
+ ui_task_runner_, socket_thread_);
+ socket->Connect(this, uuid, BluetoothSocketChromeOS::SECURITY_LEVEL_LOW,
+ base::Bind(callback, socket), error_callback);
}
void BluetoothDeviceChromeOS::CreateGattConnection(
const base::Closure& callback,
const ErrorCallback& error_callback) OVERRIDE;
+ // Attempts to initiate an insecure outgoing L2CAP or RFCOMM connection to the
+ // advertised service on this device matching |uuid|, performing an SDP lookup
+ // if necessary to determine the correct protocol and channel for the
+ // connection. Unlike ConnectToService, the outgoing connection will request
+ // no bonding rather than general bonding. |callback| will be called on a
+ // successful connection with a BluetoothSocket instance that is to be owned
+ // by the receiver. |error_callback| will be called on failure with a message
+ // indicating the cause.
+ void ConnectToServiceInsecurely(
+ const device::BluetoothUUID& uuid,
+ const ConnectToServiceCallback& callback,
+ const ConnectToServiceErrorCallback& error_callback);
+
// Creates a pairing object with the given delegate |pairing_delegate| and
// establishes it as the pairing context for this device. All pairing-related
// method calls will be forwarded to this object until it is released.
void BluetoothSocketChromeOS::Connect(
const BluetoothDeviceChromeOS* device,
const BluetoothUUID& uuid,
+ SecurityLevel security_level,
const base::Closure& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
device_path_ = device->object_path();
uuid_ = uuid;
options_.reset(new BluetoothProfileManagerClient::Options());
+ if (security_level == SECURITY_LEVEL_LOW)
+ options_->require_authentication.reset(new bool(false));
RegisterProfile(success_callback, error_callback);
}
public device::BluetoothAdapter::Observer,
public BluetoothProfileServiceProvider::Delegate {
public:
+ enum SecurityLevel {
+ SECURITY_LEVEL_LOW,
+ SECURITY_LEVEL_MEDIUM
+ };
+
static scoped_refptr<BluetoothSocketChromeOS> CreateBluetoothSocket(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
scoped_refptr<device::BluetoothSocketThread> socket_thread);
// with a message explaining the cause of the failure.
virtual void Connect(const BluetoothDeviceChromeOS* device,
const device::BluetoothUUID& uuid,
+ SecurityLevel security_level,
const base::Closure& success_callback,
const ErrorCompletionCallback& error_callback);
message_loop_ = base::MessageLoopProxy::current();
DCHECK(device_.get());
+
size_t expected_report_size = device_info.max_input_report_size;
if (device_info.has_report_id) {
expected_report_size++;
}
- inbound_buffer_.reset((uint8_t*)malloc(expected_report_size));
- IOHIDDeviceRegisterInputReportCallback(device_.get(),
- inbound_buffer_.get(),
- expected_report_size,
- &HidConnectionMac::InputReportCallback,
- this);
- IOHIDDeviceOpen(device_, kIOHIDOptionsTypeNone);
+ inbound_buffer_.resize(expected_report_size);
+ if (inbound_buffer_.size() > 0) {
+ IOHIDDeviceRegisterInputReportCallback(
+ device_.get(),
+ &inbound_buffer_[0],
+ inbound_buffer_.size(),
+ &HidConnectionMac::InputReportCallback,
+ this);
+ }
}
HidConnectionMac::~HidConnectionMac() {
- IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone);
+ if (inbound_buffer_.size() > 0) {
+ // Unregister the input report callback before this object is freed.
+ IOHIDDeviceRegisterInputReportCallback(
+ device_.get(), &inbound_buffer_[0], inbound_buffer_.size(), NULL, this);
+ }
Flush();
}
&report_size);
if (result == kIOReturnSuccess)
callback.Run(true, report_size);
- else
+ else {
+ VLOG(1) << "Failed to get feature report: " << result;
callback.Run(false, 0);
+ }
}
void HidConnectionMac::PlatformSendFeatureReport(
uint32_t report_id,
uint8_t* report_bytes,
CFIndex report_length) {
+ if (result != kIOReturnSuccess) {
+ VLOG(1) << "Failed to read input report: " << result;
+ return;
+ }
+
HidConnectionMac* connection = static_cast<HidConnectionMac*>(context);
// report_id is already contained in report_bytes
scoped_refptr<net::IOBufferWithSize> buffer;
if (res != kIOReturnSuccess) {
callback.Run(false, 0);
} else {
+ VLOG(1) << "Failed to set report: " << res;
callback.Run(true, output_buffer->size());
}
}
base::ScopedCFTypeRef<IOHIDDeviceRef> device_;
scoped_refptr<base::MessageLoopProxy> message_loop_;
- scoped_ptr<uint8_t, base::FreeDeleter> inbound_buffer_;
+ std::vector<uint8_t> inbound_buffer_;
std::queue<PendingHidReport> pending_reports_;
std::queue<PendingHidRead> pending_reads_;
serial::ReceiveError error) {
DCHECK(CalledOnValidThread());
DCHECK(IsReadPending());
+ scoped_ptr<WritableBuffer> pending_read_buffer = pending_read_buffer_.Pass();
if (error == serial::RECEIVE_ERROR_NONE) {
- pending_read_buffer_->Done(bytes_read);
+ pending_read_buffer->Done(bytes_read);
} else {
- pending_read_buffer_->DoneWithError(bytes_read, error);
+ pending_read_buffer->DoneWithError(bytes_read, error);
}
- pending_read_buffer_.reset();
Release();
}
serial::SendError error) {
DCHECK(CalledOnValidThread());
DCHECK(IsWritePending());
+ scoped_ptr<ReadOnlyBuffer> pending_write_buffer =
+ pending_write_buffer_.Pass();
if (error == serial::SEND_ERROR_NONE) {
- pending_write_buffer_->Done(bytes_written);
+ pending_write_buffer->Done(bytes_written);
} else {
- pending_write_buffer_->DoneWithError(bytes_written, error);
+ pending_write_buffer->DoneWithError(bytes_written, error);
}
- pending_write_buffer_.reset();
Release();
}
}
scoped_refptr<net::IOBufferWithSize> buffer(
new net::IOBufferWithSize(parameters_->data.size()));
+ memcpy(buffer->data(), parameters_->data.c_str(), parameters_->data.size());
resource->connection()->SendFeatureReport(
static_cast<uint8_t>(parameters_->report_id),
buffer,
relative_path_(relative_path),
key_(key),
status_(NOT_INITIALIZED),
+ content_exists_(false),
have_verified_contents_(false),
have_computed_hashes_(false),
block_size_(0) {
base::FilePath verified_contents_path =
file_util::GetVerifiedContentsPath(extension_root_);
+ // Check that this is a valid resource to verify (i.e., it exists).
+ base::FilePath content_path = extension_root_.Append(relative_path_);
+ if (!base::PathExists(content_path))
+ return false;
+
+ content_exists_ = true;
+
if (!base::PathExists(verified_contents_path))
return false;
// should likely be discarded.
bool Init();
+ // Indicates whether the content in question exists in the local extension
+ // installation. This may be |false| if Init fails.
+ bool content_exists() const { return content_exists_; }
+
// These return whether we found valid verified_contents.json /
// computed_hashes.json files respectively. Note that both of these can be
// true but we still didn't find an entry for |relative_path_| in them.
- bool have_verified_contents() { return have_verified_contents_; }
- bool have_computed_hashes() { return have_computed_hashes_; }
+ bool have_verified_contents() const { return have_verified_contents_; }
+ bool have_computed_hashes() const { return have_computed_hashes_; }
// Return the number of blocks and block size, respectively. Only valid after
// calling Init().
InitStatus status_;
+ bool content_exists_;
+
bool have_verified_contents_;
bool have_computed_hashes_;
void ContentVerifyJob::OnHashesReady(bool success) {
if (!success && !g_test_delegate) {
- if (hash_reader_->have_verified_contents() &&
- hash_reader_->have_computed_hashes())
+ if (!hash_reader_->content_exists()) {
+ // Ignore verification of non-existent resources.
+ return;
+ } else if (hash_reader_->have_verified_contents() &&
+ hash_reader_->have_computed_hashes()) {
DispatchFailureCallback(NO_HASHES_FOR_FILE);
- else
+ } else {
DispatchFailureCallback(MISSING_ALL_HASHES);
+ }
return;
}
EASYUNLOCKPRIVATE_CLEARPERMITACCESS,
EASYUNLOCKPRIVATE_SETREMOTEDEVICES,
EASYUNLOCKPRIVATE_GETREMOTEDEVICES,
+ FILESYSTEMPROVIDER_GETALL,
+ EASYUNLOCKPRIVATE_CONNECTTOBLUETOOTHSERVICEINSECURELY,
// Last entry: Add new entries above and ensure to update
// tools/metrics/histograms/histograms.xml.
ENUM_BOUNDARY
case net::HTTP_OK:
break;
case net::HTTP_FORBIDDEN:
- case net::HTTP_INTERNAL_SERVER_ERROR:
// HTTP_FORBIDDEN (403) is treated as temporary error, because it may be
- // '403 Rate Limit Exeeded.' 500 is always treated as transient.
+ // '403 Rate Limit Exeeded.'
OnGetTokenFailure(
GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
return;
: GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_ERROR));
return;
}
- default:
- // The other errors are treated as permanent error.
- OnGetTokenFailure(GoogleServiceAuthError(
- GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+ default: {
+ if (source->GetResponseCode() >= net::HTTP_INTERNAL_SERVER_ERROR) {
+ // 5xx is always treated as transient.
+ OnGetTokenFailure(GoogleServiceAuthError(
+ GoogleServiceAuthError::SERVICE_UNAVAILABLE));
+ } else {
+ // The other errors are treated as permanent error.
+ DLOG(ERROR) << "Unexpected persistent error: http_status="
+ << source->GetResponseCode();
+ OnGetTokenFailure(GoogleServiceAuthError(
+ GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+ }
return;
+ }
}
// The request was successfully fetched and it returned OK.
void ConnectionFactoryImpl::ReportSuccessfulProxyConnection() {
if (gcm_network_session_ && gcm_network_session_->proxy_service())
- gcm_network_session_->proxy_service()->ReportSuccess(proxy_info_);
+ gcm_network_session_->proxy_service()->ReportSuccess(proxy_info_, NULL);
}
void ConnectionFactoryImpl::CloseSocket() {
namespace {
-const int32 kCommandBufferSize = 1024 * 1024;
-// TODO(kbr): make the transfer buffer size configurable via context
-// creation attributes.
-const size_t kStartTransferBufferSize = 4 * 1024 * 1024;
-const size_t kMinTransferBufferSize = 1 * 256 * 1024;
-const size_t kMaxTransferBufferSize = 16 * 1024 * 1024;
+const int32 kDefaultCommandBufferSize = 1024 * 1024;
+const unsigned int kDefaultStartTransferBufferSize = 4 * 1024 * 1024;
+const unsigned int kDefaultMinTransferBufferSize = 1 * 256 * 1024;
+const unsigned int kDefaultMaxTransferBufferSize = 16 * 1024 * 1024;
class GLInProcessContextImpl
: public GLInProcessContext,
public base::SupportsWeakPtr<GLInProcessContextImpl> {
public:
- explicit GLInProcessContextImpl();
+ explicit GLInProcessContextImpl(
+ const GLInProcessContextSharedMemoryLimits& mem_limits);
virtual ~GLInProcessContextImpl();
bool Initialize(
// GLInProcessContext implementation:
virtual void SetContextLostCallback(const base::Closure& callback) OVERRIDE;
virtual gles2::GLES2Implementation* GetImplementation() OVERRIDE;
+ virtual size_t GetMappedMemoryLimit() OVERRIDE;
#if defined(OS_ANDROID)
virtual scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture(
scoped_ptr<gles2::GLES2Implementation> gles2_implementation_;
scoped_ptr<InProcessCommandBuffer> command_buffer_;
+ const GLInProcessContextSharedMemoryLimits mem_limits_;
bool context_lost_;
base::Closure context_lost_callback_;
base::LazyInstance<std::set<GLInProcessContextImpl*> > g_all_shared_contexts =
LAZY_INSTANCE_INITIALIZER;
-GLInProcessContextImpl::GLInProcessContextImpl()
- : context_lost_(false) {}
+GLInProcessContextImpl::GLInProcessContextImpl(
+ const GLInProcessContextSharedMemoryLimits& mem_limits)
+ : mem_limits_(mem_limits), context_lost_(false) {
+}
GLInProcessContextImpl::~GLInProcessContextImpl() {
{
return gles2_implementation_.get();
}
+size_t GLInProcessContextImpl::GetMappedMemoryLimit() {
+ return mem_limits_.mapped_memory_reclaim_limit;
+}
+
void GLInProcessContextImpl::SetContextLostCallback(
const base::Closure& callback) {
context_lost_callback_ = callback;
// Create the GLES2 helper, which writes the command buffer protocol.
gles2_helper_.reset(new gles2::GLES2CmdHelper(command_buffer_.get()));
- if (!gles2_helper_->Initialize(kCommandBufferSize)) {
+ if (!gles2_helper_->Initialize(mem_limits_.command_buffer_size)) {
LOG(ERROR) << "Failed to initialize GLES2CmdHelper";
Destroy();
return false;
}
if (!gles2_implementation_->Initialize(
- kStartTransferBufferSize,
- kMinTransferBufferSize,
- kMaxTransferBufferSize,
- gles2::GLES2Implementation::kNoLimit)) {
+ mem_limits_.start_transfer_buffer_size,
+ mem_limits_.min_transfer_buffer_size,
+ mem_limits_.max_transfer_buffer_size,
+ mem_limits_.mapped_memory_reclaim_limit)) {
return false;
}
} // anonymous namespace
+GLInProcessContextSharedMemoryLimits::GLInProcessContextSharedMemoryLimits()
+ : command_buffer_size(kDefaultCommandBufferSize),
+ start_transfer_buffer_size(kDefaultStartTransferBufferSize),
+ min_transfer_buffer_size(kDefaultMinTransferBufferSize),
+ max_transfer_buffer_size(kDefaultMaxTransferBufferSize),
+ mapped_memory_reclaim_limit(gles2::GLES2Implementation::kNoLimit) {
+}
+
+// static
GLInProcessContext* GLInProcessContext::Create(
scoped_refptr<gpu::InProcessCommandBuffer::Service> service,
scoped_refptr<gfx::GLSurface> surface,
GLInProcessContext* share_context,
bool use_global_share_group,
const ::gpu::gles2::ContextCreationAttribHelper& attribs,
- gfx::GpuPreference gpu_preference) {
+ gfx::GpuPreference gpu_preference,
+ const GLInProcessContextSharedMemoryLimits& memory_limits) {
DCHECK(!use_global_share_group || !share_context);
if (surface.get()) {
DCHECK_EQ(surface->IsOffscreen(), is_offscreen);
DCHECK_EQ(gfx::kNullAcceleratedWidget, window);
}
- scoped_ptr<GLInProcessContextImpl> context(new GLInProcessContextImpl());
+ scoped_ptr<GLInProcessContextImpl> context(
+ new GLInProcessContextImpl(memory_limits));
if (!context->Initialize(surface,
is_offscreen,
use_global_share_group,
class GLES2Implementation;
}
+struct GL_IN_PROCESS_CONTEXT_EXPORT GLInProcessContextSharedMemoryLimits {
+ GLInProcessContextSharedMemoryLimits();
+
+ int32 command_buffer_size;
+ unsigned int start_transfer_buffer_size;
+ unsigned int min_transfer_buffer_size;
+ unsigned int max_transfer_buffer_size;
+ unsigned int mapped_memory_reclaim_limit;
+};
+
class GL_IN_PROCESS_CONTEXT_EXPORT GLInProcessContext {
public:
virtual ~GLInProcessContext() {}
GLInProcessContext* share_context,
bool use_global_share_group,
const gpu::gles2::ContextCreationAttribHelper& attribs,
- gfx::GpuPreference gpu_preference);
+ gfx::GpuPreference gpu_preference,
+ const GLInProcessContextSharedMemoryLimits& memory_limits);
virtual void SetContextLostCallback(const base::Closure& callback) = 0;
// can be used without making it current.
virtual gles2::GLES2Implementation* GetImplementation() = 0;
+ virtual size_t GetMappedMemoryLimit() = 0;
+
#if defined(OS_ANDROID)
virtual scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture(
uint32 stream_id) = 0;
namespace gpu {
namespace {
-bool IsBroadcom() {
- const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
- if (vendor)
- return std::string(vendor).find("Broadcom") != std::string::npos;
- return false;
+enum GpuType {
+ GPU_BROADCOM,
+ GPU_IMAGINATION,
+ GPU_NVIDIA_ES31,
+ GPU_ADRENO_420,
+ GPU_OTHER,
+};
+
+std::string MakeString(const char* s) {
+ return std::string(s ? s : "");
}
-bool IsImagination() {
- const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
- if (vendor)
- return std::string(vendor).find("Imagination") != std::string::npos;
- return false;
+GpuType GetGpuType() {
+ const std::string vendor = MakeString(
+ reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
+ const std::string renderer = MakeString(
+ reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
+ const std::string version = MakeString(
+ reinterpret_cast<const char*>(glGetString(GL_VERSION)));
+
+ if (vendor.find("Broadcom") != std::string::npos)
+ return GPU_BROADCOM;
+
+ if (vendor.find("Imagination") != std::string::npos)
+ return GPU_IMAGINATION;
+
+ if (vendor.find("NVIDIA") != std::string::npos &&
+ version.find("OpenGL ES 3.1") != std::string::npos) {
+ return GPU_NVIDIA_ES31;
+ }
+
+ if (vendor.find("Qualcomm") != std::string::npos &&
+ renderer.find("Adreno (TM) 420") != std::string::npos) {
+ return GPU_ADRENO_420;
+ }
+
+ return GPU_OTHER;
}
-bool IsNvidia31() {
- const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
- const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
- return vendor && version &&
- std::string(vendor).find("NVIDIA") != std::string::npos &&
- std::string(version).find("OpenGL ES 3.1") != std::string::npos;
+bool AllowTransferThreadForGpu() {
+ GpuType gpu = GetGpuType();
+ return gpu != GPU_BROADCOM && gpu != GPU_IMAGINATION &&
+ gpu != GPU_NVIDIA_ES31 && gpu != GPU_ADRENO_420;
}
}
// resolution of crbug.com/271929
AsyncPixelTransferManager* AsyncPixelTransferManager::Create(
gfx::GLContext* context) {
- TRACE_EVENT0("gpu", "AsyncPixelTransferManager::Create");
+ DCHECK(context->IsCurrent(NULL));
switch (gfx::GetGLImplementation()) {
case gfx::kGLImplementationEGLGLES2:
DCHECK(context);
- if (context->HasExtension("EGL_KHR_fence_sync") &&
+ if (!base::SysInfo::IsLowEndDevice() &&
+ context->HasExtension("EGL_KHR_fence_sync") &&
context->HasExtension("EGL_KHR_image") &&
context->HasExtension("EGL_KHR_image_base") &&
context->HasExtension("EGL_KHR_gl_texture_2D_image") &&
context->HasExtension("GL_OES_EGL_image") &&
- !IsBroadcom() &&
- !IsImagination() &&
- !IsNvidia31() &&
- !base::SysInfo::IsLowEndDevice()) {
+ AllowTransferThreadForGpu()) {
+ TRACE_EVENT0("gpu", "AsyncPixelTransferManager_CreateWithThread");
return new AsyncPixelTransferManagerEGL;
}
return new AsyncPixelTransferManagerIdle;
- case gfx::kGLImplementationOSMesaGL:
+ case gfx::kGLImplementationOSMesaGL: {
+ TRACE_EVENT0("gpu", "AsyncPixelTransferManager_CreateIdle");
return new AsyncPixelTransferManagerIdle;
+ }
case gfx::kGLImplementationMockGL:
return new AsyncPixelTransferManagerStub;
default:
CheckSequencedThread();
idle_work_pending_ = false;
base::AutoLock lock(command_buffer_lock_);
- if (gpu_scheduler_->HasMoreWork()) {
+ if (MakeCurrent() && gpu_scheduler_->HasMoreWork()) {
gpu_scheduler_->PerformIdleWork();
ScheduleIdleWorkOnGpuThread();
}
{
"name": "software rendering list",
// Please update the version number whenever you change this file.
- "version": "9.3",
+ "version": "9.7",
"entries": [
{
"id": 1,
},
{
"id": 83,
- "description": "Samsung Gaxlaxy NOTE II is too buggy to use for video decoding",
+ "description": "Samsung Galaxy NOTE is too buggy to use for video decoding",
"cr_bugs": [308721],
"os": {
- "type": "android",
- "version": {
- "op": "<=",
- "value": "4.1.2"
- }
+ "type": "android"
},
- "machine_model_name": ["GT-N7100"],
+ "machine_model_name": ["GT-.*"],
"features": [
"accelerated_video_decode"
]
},
{
"id": 85,
- "description": "Samsung Gaxlaxy S4 is too buggy to use for video decoding",
+ "description": "Samsung Galaxy S4 is too buggy to use for video decoding",
"cr_bugs": [329072],
"os": {
"type": "android"
},
- "machine_model_name": ["SCH-I545"],
+ "machine_model_name": ["SCH-.*"],
"features": [
"accelerated_video_decode"
]
},
"machine_model_name": ["HTC One",
"C5303", "C6603", "C6903",
- "GT-I8262", "GT-I8552", "GT-I9195", "GT-I9300",
- "GT-I9500", "GT-I9505", "GT-N7100",
+ "GT-I8262", "GT-I8552", "GT-I9195",
+ "GT-I9500", "GT-I9505",
"SAMSUNG-SCH-I337", "SCH-I545", "SGH-M919",
"SM-N900", "SM-N9005", "SPH-L720",
"XT907", "XT1032", "XT1033", "XT1080"]
"features": [
"gpu_rasterization"
]
+ },
+ {
+ "id": 100,
+ "description": "GPU rasterization is blacklisted on Nexus 10",
+ "cr_bugs": [407144],
+ "gl_renderer": ".*Mali-T604.*",
+ "features": [
+ "gpu_rasterization"
+ ]
+ },
+ {
+ "id": 101,
+ "description": "Samsung Galaxy Tab is too buggy to use for video decoding",
+ "cr_bugs": [408353],
+ "os": {
+ "type": "android"
+ },
+ "machine_model_name": ["SM-.*"],
+ "features": [
+ "accelerated_video_decode"
+ ]
}
]
}
// Safari version can't be, so a lookup table is used instead (for both, since
// the reported versions should stay in sync).
static const OSVersionMap version_map[] = {
+ { 8, 0, { "600.1.4", "600.1.4" } },
{ 7, 1, { "9537.53", "537.51.2" } },
{ 7, 0, { "9537.53", "537.51.1" } },
// 6.1 has the same values as 6.0.
validate_client_(false),
writing_(false),
debug_flags_(0),
+ write_error_(0),
+ last_write_error_(0),
+ write_size_(0),
client_secret_(0),
weak_factory_(this) {
CreatePipe(channel_handle, mode);
debug_flags_ |= WRITE_MSG;
CHECK(!writing_);
writing_ = true;
+ write_size_ = static_cast<uint32>(m->size());
+ write_error_ = 0;
BOOL ok = WriteFile(pipe_,
m->data(),
- static_cast<int>(m->size()),
- &bytes_written,
+ write_size_,
+ NULL,
&output_state_.context.overlapped);
if (!ok) {
- DWORD err = GetLastError();
- if (err == ERROR_IO_PENDING) {
+ write_error_ = GetLastError();
+ if (write_error_ == ERROR_IO_PENDING) {
output_state_.is_pending = true;
DVLOG(2) << "sent pending message @" << m << " on channel @" << this
return true;
}
writing_ = false;
- LOG(ERROR) << "pipe error: " << err;
+ last_write_error_ = write_error_;
+ LOG(ERROR) << "pipe error: " << write_error_;
return false;
}
bool writing_;
// Tracks the lifetime of this object, for debugging purposes.
- int32 debug_flags_;
+ uint32 debug_flags_;
+
+ // OS result for the current write. TODO(rvargas): remove this.
+ uint32 write_error_;
+
+ // OS result for a previous failed write. TODO(rvargas): remove this.
+ uint32 last_write_error_;
+
+ // Size of the current write. TODO(rvargas): remove this.
+ uint32 write_size_;
// This is a unique per-channel value used to authenticate the client end of
// a connection. If the value is non-zero, the client passes it in the hello
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
+#include "net/http/http_auth_controller.h"
#include "net/http/http_network_session.h"
+#include "net/http/proxy_client_socket.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/client_socket_pool_manager.h"
#include "net/url_request/url_request_context.h"
// "address unreachable" error, and will report both of these failures as
// ERR_ADDRESS_UNREACHABLE.
return net::ERR_ADDRESS_UNREACHABLE;
+ case net::ERR_PROXY_AUTH_REQUESTED: {
+ net::ProxyClientSocket* proxy_socket =
+ static_cast<net::ProxyClientSocket*>(transport_->socket());
+
+ if (proxy_socket->GetAuthController()->HaveAuth())
+ return proxy_socket->RestartWithAuth(connect_callback_);
+
+ return error;
+ }
default:
return error;
}
}
void ProxyResolvingClientSocket::ReportSuccessfulProxyConnection() {
- network_session_->proxy_service()->ReportSuccess(proxy_info_);
+ network_session_->proxy_service()->ReportSuccess(proxy_info_, NULL);
}
void ProxyResolvingClientSocket::Disconnect() {
#include "base/strings/stringprintf.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
+#include "media/audio/audio_parameters.h"
#include "media/base/limits.h"
#include "media/base/scoped_histogram_timer.h"
#include "media/base/user_input_monitor.h"
max_volume_(0.0),
user_input_monitor_(user_input_monitor),
#if defined(AUDIO_POWER_MONITORING)
+ log_silence_state_(false),
silence_state_(SILENCE_STATE_NO_MEASUREMENT),
#endif
prev_key_down_count_(0) {
// Create and open a new audio input stream from the existing
// audio-device thread.
- if (!controller->task_runner_->PostTask(FROM_HERE,
- base::Bind(&AudioInputController::DoCreate, controller,
- base::Unretained(audio_manager), params, device_id))) {
+ if (!controller->task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&AudioInputController::DoCreate,
+ controller,
+ base::Unretained(audio_manager),
+ params,
+ device_id))) {
controller = NULL;
}
// Create and open a new audio input stream from the existing
// audio-device thread. Use the provided audio-input device.
- if (!controller->task_runner_->PostTask(FROM_HERE,
- base::Bind(&AudioInputController::DoCreate, controller,
- base::Unretained(audio_manager), params, device_id))) {
+ if (!controller->task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&AudioInputController::DoCreateForLowLatency,
+ controller,
+ base::Unretained(audio_manager),
+ params,
+ device_id))) {
controller = NULL;
}
const std::string& device_id) {
DCHECK(task_runner_->BelongsToCurrentThread());
SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CreateTime");
+ if (handler_)
+ handler_->OnLog(this, "AIC::DoCreate");
#if defined(AUDIO_POWER_MONITORING)
// Create the audio (power) level meter given the provided audio parameters.
// An AudioBus is also needed to wrap the raw data buffer from the native
// layer to match AudioPowerMonitor::Scan().
// TODO(henrika): Remove use of extra AudioBus. See http://crbug.com/375155.
+ last_audio_level_log_time_ = base::TimeTicks::Now();
audio_level_.reset(new media::AudioPowerMonitor(
params.sample_rate(),
TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMilliseconds)));
DoCreateForStream(audio_manager->MakeAudioInputStream(params, device_id));
}
+void AudioInputController::DoCreateForLowLatency(AudioManager* audio_manager,
+ const AudioParameters& params,
+ const std::string& device_id) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+#if defined(AUDIO_POWER_MONITORING)
+ // We only log silence state UMA stats for low latency mode and if we use a
+ // real device.
+ if (params.format() != AudioParameters::AUDIO_FAKE)
+ log_silence_state_ = true;
+#endif
+
+ DoCreate(audio_manager, params, device_id);
+}
+
void AudioInputController::DoCreateForStream(
AudioInputStream* stream_to_control) {
DCHECK(task_runner_->BelongsToCurrentThread());
state_ = RECORDING;
}
+ if (handler_)
+ handler_->OnLog(this, "AIC::DoRecord");
+
if (no_data_timer_) {
// Start the data timer. Once |kTimerResetIntervalSeconds| have passed,
// a callback to FirstCheckForNoData() is made.
if (state_ == CLOSED)
return;
+ if (handler_)
+ handler_->OnLog(this, "AIC::DoClose");
+
// Delete the timer on the same thread that created it.
no_data_timer_.reset();
user_input_monitor_->DisableKeyPressMonitoring();
#if defined(AUDIO_POWER_MONITORING)
- // Send UMA stats if we have enabled power monitoring.
- if (audio_level_) {
+ // Send UMA stats if enabled.
+ if (log_silence_state_)
LogSilenceState(silence_state_);
- }
+ log_silence_state_ = false;
#endif
state_ = CLOSED;
LogCaptureStartupResult(GetDataIsActive() ?
CAPTURE_STARTUP_OK :
CAPTURE_STARTUP_NO_DATA_CALLBACK);
+ if (handler_) {
+ handler_->OnLog(this, GetDataIsActive() ?
+ "AIC::FirstCheckForNoData => data is active" :
+ "AIC::FirstCheckForNoData => data is NOT active");
+ }
DoCheckForNoData();
}
std::string log_string = base::StringPrintf(
"AIC::OnData: average audio level=%.2f dBFS", level_dbfs);
static const float kSilenceThresholdDBFS = -72.24719896f;
- if (level_dbfs < kSilenceThresholdDBFS) {
+ if (level_dbfs < kSilenceThresholdDBFS)
log_string += " <=> no audio input!";
- if (silence_state_ == SILENCE_STATE_NO_MEASUREMENT)
- silence_state_ = SILENCE_STATE_ONLY_SILENCE;
- else if (silence_state_ == SILENCE_STATE_ONLY_AUDIO)
- silence_state_ = SILENCE_STATE_AUDIO_AND_SILENCE;
- else
- DCHECK(silence_state_ == SILENCE_STATE_ONLY_SILENCE ||
- silence_state_ == SILENCE_STATE_AUDIO_AND_SILENCE);
- } else {
- if (silence_state_ == SILENCE_STATE_NO_MEASUREMENT)
- silence_state_ = SILENCE_STATE_ONLY_AUDIO;
- else if (silence_state_ == SILENCE_STATE_ONLY_SILENCE)
- silence_state_ = SILENCE_STATE_AUDIO_AND_SILENCE;
- else
- DCHECK(silence_state_ == SILENCE_STATE_ONLY_AUDIO ||
- silence_state_ == SILENCE_STATE_AUDIO_AND_SILENCE);
- }
-
handler_->OnLog(this, log_string);
+
+ UpdateSilenceState(level_dbfs < kSilenceThresholdDBFS);
#endif
}
}
#if defined(AUDIO_POWER_MONITORING)
+void AudioInputController::UpdateSilenceState(bool silence) {
+ if (silence) {
+ if (silence_state_ == SILENCE_STATE_NO_MEASUREMENT) {
+ silence_state_ = SILENCE_STATE_ONLY_SILENCE;
+ } else if (silence_state_ == SILENCE_STATE_ONLY_AUDIO) {
+ silence_state_ = SILENCE_STATE_AUDIO_AND_SILENCE;
+ } else {
+ DCHECK(silence_state_ == SILENCE_STATE_ONLY_SILENCE ||
+ silence_state_ == SILENCE_STATE_AUDIO_AND_SILENCE);
+ }
+ } else {
+ if (silence_state_ == SILENCE_STATE_NO_MEASUREMENT) {
+ silence_state_ = SILENCE_STATE_ONLY_AUDIO;
+ } else if (silence_state_ == SILENCE_STATE_ONLY_SILENCE) {
+ silence_state_ = SILENCE_STATE_AUDIO_AND_SILENCE;
+ } else {
+ DCHECK(silence_state_ == SILENCE_STATE_ONLY_AUDIO ||
+ silence_state_ == SILENCE_STATE_AUDIO_AND_SILENCE);
+ }
+ }
+}
+
void AudioInputController::LogSilenceState(SilenceState value) {
UMA_HISTOGRAM_ENUMERATION("Media.AudioInputControllerSessionSilenceReport",
value,
SyncWriter* sync_writer,
UserInputMonitor* user_input_monitor);
- // Factory method for creating an AudioInputController for low-latency mode,
- // taking ownership of |stream|. The stream will be opened on the audio
- // thread, and when that is done, the event handler will receive an
- // OnCreated() call from that same thread. |user_input_monitor| is used for
- // typing detection and can be NULL.
+ // Factory method for creating an AudioInputController with an existing
+ // |stream| for low-latency mode, taking ownership of |stream|. The stream
+ // will be opened on the audio thread, and when that is done, the event
+ // handler will receive an OnCreated() call from that same thread.
+ // |user_input_monitor| is used for typing detection and can be NULL.
static scoped_refptr<AudioInputController> CreateForStream(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
EventHandler* event_handler,
// Elements in this enum should not be deleted or rearranged; the only
// permitted operation is to add new elements before SILENCE_STATE_MAX and
// update SILENCE_STATE_MAX.
+ // Possible silence state transitions:
+ // SILENCE_STATE_AUDIO_AND_SILENCE
+ // ^ ^
+ // SILENCE_STATE_ONLY_AUDIO SILENCE_STATE_ONLY_SILENCE
+ // ^ ^
+ // SILENCE_STATE_NO_MEASUREMENT
enum SilenceState {
SILENCE_STATE_NO_MEASUREMENT = 0,
SILENCE_STATE_ONLY_AUDIO = 1,
virtual ~AudioInputController();
// Methods called on the audio thread (owned by the AudioManager).
- void DoCreate(AudioManager* audio_manager, const AudioParameters& params,
+ void DoCreate(AudioManager* audio_manager,
+ const AudioParameters& params,
const std::string& device_id);
+ void DoCreateForLowLatency(AudioManager* audio_manager,
+ const AudioParameters& params,
+ const std::string& device_id);
void DoCreateForStream(AudioInputStream* stream_to_control);
void DoRecord();
void DoClose();
bool GetDataIsActive();
#if defined(AUDIO_POWER_MONITORING)
+ // Updates the silence state, see enum SilenceState above for state
+ // transitions.
+ void UpdateSilenceState(bool silence);
+
+ // Logs the silence state as UMA stat.
void LogSilenceState(SilenceState value);
#endif
media::AudioParameters audio_params_;
base::TimeTicks last_audio_level_log_time_;
+ // Whether the silence state should sent as UMA stat.
+ bool log_silence_state_;
+
// The silence report sent as UMA stat at the end of a session.
SilenceState silence_state_;
#endif
return false;
}
- // Register the input procedure for the AUHAL.
- // This procedure will be called when the AUHAL has received new data
- // from the input device.
- AURenderCallbackStruct callback;
- callback.inputProc = InputProc;
- callback.inputProcRefCon = this;
- result = AudioUnitSetProperty(audio_unit_,
- kAudioOutputUnitProperty_SetInputCallback,
- kAudioUnitScope_Global,
- 0,
- &callback,
- sizeof(callback));
- if (result) {
- HandleError(result);
- return false;
- }
-
// Set up the the desired (output) format.
// For obtaining input from a device, the device format is always expressed
// on the output scope of the AUHAL's Element 1.
}
}
+ // Register the input procedure for the AUHAL.
+ // This procedure will be called when the AUHAL has received new data
+ // from the input device.
+ AURenderCallbackStruct callback;
+ callback.inputProc = InputProc;
+ callback.inputProcRefCon = this;
+ result = AudioUnitSetProperty(audio_unit_,
+ kAudioOutputUnitProperty_SetInputCallback,
+ kAudioUnitScope_Global,
+ 0,
+ &callback,
+ sizeof(callback));
+ if (result) {
+ HandleError(result);
+ return false;
+ }
+
// Finally, initialize the audio unit and ensure that it is ready to render.
// Allocates memory according to the maximum number of audio frames
// it can produce in response to a single render call.
}
if (audio_unit_) {
// Deallocate the audio unit’s resources.
- AudioUnitUninitialize(audio_unit_);
+ OSStatus result = AudioUnitUninitialize(audio_unit_);
+ OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+ << "AudioUnitUninitialize() failed.";
+
+ result = AudioComponentInstanceDispose(audio_unit_);
+ OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+ << "AudioComponentInstanceDispose() failed.";
- // Terminates our connection to the AUHAL component.
- CloseComponent(audio_unit_);
audio_unit_ = 0;
}
case 192000:
*asr = k192000Hz;
return true;
+ case 24000:
+ *asr = k24000Hz;
+ return true;
}
return false;
}
k88200Hz = 8,
k176400Hz = 9,
k192000Hz = 10,
+ k24000Hz = 11,
// Must always equal the largest value ever reported:
- kAudioSampleRateMax = k192000Hz,
+ kAudioSampleRateMax = k24000Hz,
};
// Helper method to convert integral values to their respective enum values,
reject_cb_.Run(exception_code, system_code, error_message);
}
-template <typename T>
-CdmPromiseTemplate<T>::CdmPromiseTemplate(
- base::Callback<void(const T&)> resolve_cb,
+CdmPromiseTemplate<void>::CdmPromiseTemplate(base::Callback<void()> resolve_cb,
+ PromiseRejectedCB reject_cb)
+ : CdmPromise(reject_cb), resolve_cb_(resolve_cb) {
+ DCHECK(!resolve_cb_.is_null());
+}
+
+CdmPromiseTemplate<void>::CdmPromiseTemplate(base::Callback<void()> resolve_cb,
+ PromiseRejectedCB reject_cb,
+ const std::string& uma_name)
+ : CdmPromise(reject_cb, uma_name), resolve_cb_(resolve_cb) {
+ DCHECK(!resolve_cb_.is_null());
+ DCHECK(!uma_name_.empty());
+}
+
+CdmPromiseTemplate<void>::CdmPromiseTemplate() {
+}
+
+CdmPromiseTemplate<void>::~CdmPromiseTemplate() {
+ DCHECK(!is_pending_);
+}
+
+void CdmPromiseTemplate<void>::resolve() {
+ DCHECK(is_pending_);
+ is_pending_ = false;
+ if (!uma_name_.empty()) {
+ base::LinearHistogram::FactoryGet(
+ uma_name_, 1, NUM_RESULT_CODES, NUM_RESULT_CODES + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag)->Add(SUCCESS);
+ }
+ resolve_cb_.Run();
+}
+
+CdmPromise::ResolveParameterType
+CdmPromiseTemplate<void>::GetResolveParameterType() const {
+ return VOID_TYPE;
+}
+
+CdmPromiseTemplate<std::string>::CdmPromiseTemplate(
+ base::Callback<void(const std::string&)> resolve_cb,
PromiseRejectedCB reject_cb)
: CdmPromise(reject_cb), resolve_cb_(resolve_cb) {
DCHECK(!resolve_cb_.is_null());
}
-template <typename T>
-CdmPromiseTemplate<T>::CdmPromiseTemplate(
- base::Callback<void(const T&)> resolve_cb,
+CdmPromiseTemplate<std::string>::CdmPromiseTemplate(
+ base::Callback<void(const std::string&)> resolve_cb,
PromiseRejectedCB reject_cb,
const std::string& uma_name)
: CdmPromise(reject_cb, uma_name), resolve_cb_(resolve_cb) {
DCHECK(!resolve_cb_.is_null());
}
-template <typename T>
-CdmPromiseTemplate<T>::CdmPromiseTemplate() {
+CdmPromiseTemplate<std::string>::CdmPromiseTemplate() {
}
-template <typename T>
-CdmPromiseTemplate<T>::~CdmPromiseTemplate() {
+CdmPromiseTemplate<std::string>::~CdmPromiseTemplate() {
DCHECK(!is_pending_);
}
-template <typename T>
-void CdmPromiseTemplate<T>::resolve(const T& result) {
+void CdmPromiseTemplate<std::string>::resolve(const std::string& result) {
DCHECK(is_pending_);
is_pending_ = false;
if (!uma_name_.empty()) {
resolve_cb_.Run(result);
}
-CdmPromiseTemplate<void>::CdmPromiseTemplate(base::Callback<void()> resolve_cb,
- PromiseRejectedCB reject_cb)
+CdmPromise::ResolveParameterType
+CdmPromiseTemplate<std::string>::GetResolveParameterType() const {
+ return STRING_TYPE;
+}
+
+CdmPromiseTemplate<KeyIdsVector>::CdmPromiseTemplate(
+ base::Callback<void(const KeyIdsVector&)> resolve_cb,
+ PromiseRejectedCB reject_cb)
: CdmPromise(reject_cb), resolve_cb_(resolve_cb) {
DCHECK(!resolve_cb_.is_null());
}
-CdmPromiseTemplate<void>::CdmPromiseTemplate(base::Callback<void()> resolve_cb,
- PromiseRejectedCB reject_cb,
- const std::string& uma_name)
+CdmPromiseTemplate<KeyIdsVector>::CdmPromiseTemplate(
+ base::Callback<void(const KeyIdsVector&)> resolve_cb,
+ PromiseRejectedCB reject_cb,
+ const std::string& uma_name)
: CdmPromise(reject_cb, uma_name), resolve_cb_(resolve_cb) {
DCHECK(!resolve_cb_.is_null());
- DCHECK(!uma_name_.empty());
}
-CdmPromiseTemplate<void>::CdmPromiseTemplate() {
-}
-
-CdmPromiseTemplate<void>::~CdmPromiseTemplate() {
+CdmPromiseTemplate<KeyIdsVector>::~CdmPromiseTemplate() {
DCHECK(!is_pending_);
}
-void CdmPromiseTemplate<void>::resolve() {
+void CdmPromiseTemplate<KeyIdsVector>::resolve(const KeyIdsVector& result) {
DCHECK(is_pending_);
is_pending_ = false;
if (!uma_name_.empty()) {
uma_name_, 1, NUM_RESULT_CODES, NUM_RESULT_CODES + 1,
base::HistogramBase::kUmaTargetedHistogramFlag)->Add(SUCCESS);
}
- resolve_cb_.Run();
+ resolve_cb_.Run(result);
}
-// Explicit template instantiation for the Promises needed.
-template class MEDIA_EXPORT CdmPromiseTemplate<std::string>;
-template class MEDIA_EXPORT CdmPromiseTemplate<KeyIdsVector>;
+CdmPromise::ResolveParameterType
+CdmPromiseTemplate<KeyIdsVector>::GetResolveParameterType() const {
+ return KEY_IDS_VECTOR_TYPE;
+}
} // namespace media
NUM_RESULT_CODES
};
+ enum ResolveParameterType {
+ VOID_TYPE,
+ STRING_TYPE,
+ KEY_IDS_VECTOR_TYPE
+ };
+
typedef base::Callback<void(MediaKeys::Exception exception_code,
uint32 system_code,
const std::string& error_message)>
uint32 system_code,
const std::string& error_message);
+ virtual ResolveParameterType GetResolveParameterType() const = 0;
+
protected:
CdmPromise();
CdmPromise(PromiseRejectedCB reject_cb);
DISALLOW_COPY_AND_ASSIGN(CdmPromise);
};
-template <typename T>
-class MEDIA_EXPORT CdmPromiseTemplate : public CdmPromise {
+// Specialization for no parameter to resolve().
+template <>
+class MEDIA_EXPORT CdmPromiseTemplate<void> : public CdmPromise {
public:
- CdmPromiseTemplate(base::Callback<void(const T&)> resolve_cb,
+ CdmPromiseTemplate(base::Callback<void(void)> resolve_cb,
PromiseRejectedCB rejected_cb);
- CdmPromiseTemplate(base::Callback<void(const T&)> resolve_cb,
+ CdmPromiseTemplate(base::Callback<void(void)> resolve_cb,
PromiseRejectedCB rejected_cb,
const std::string& uma_name);
virtual ~CdmPromiseTemplate();
- virtual void resolve(const T& result);
+ virtual void resolve();
+ virtual ResolveParameterType GetResolveParameterType() const OVERRIDE;
protected:
// Allow subclasses to completely override the implementation.
- // TODO(jrummell): Remove when derived class SessionLoadedPromise
- // (in ppapi_decryptor.cc) is no longer needed.
CdmPromiseTemplate();
private:
- base::Callback<void(const T&)> resolve_cb_;
+ base::Callback<void(void)> resolve_cb_;
DISALLOW_COPY_AND_ASSIGN(CdmPromiseTemplate);
};
-// Specialization for no parameter to resolve().
template <>
-class MEDIA_EXPORT CdmPromiseTemplate<void> : public CdmPromise {
+class MEDIA_EXPORT CdmPromiseTemplate<std::string> : public CdmPromise {
public:
- CdmPromiseTemplate(base::Callback<void(void)> resolve_cb,
+ CdmPromiseTemplate(base::Callback<void(const std::string&)> resolve_cb,
PromiseRejectedCB rejected_cb);
- CdmPromiseTemplate(base::Callback<void(void)> resolve_cb,
+ CdmPromiseTemplate(base::Callback<void(const std::string&)> resolve_cb,
PromiseRejectedCB rejected_cb,
const std::string& uma_name);
virtual ~CdmPromiseTemplate();
- virtual void resolve();
+ virtual void resolve(const std::string& result);
+ virtual ResolveParameterType GetResolveParameterType() const OVERRIDE;
protected:
// Allow subclasses to completely override the implementation.
+ // TODO(jrummell): Remove when derived class SessionLoadedPromise
+ // (in ppapi_decryptor.cc) is no longer needed.
CdmPromiseTemplate();
private:
- base::Callback<void(void)> resolve_cb_;
+ base::Callback<void(const std::string&)> resolve_cb_;
+
+ DISALLOW_COPY_AND_ASSIGN(CdmPromiseTemplate);
+};
+
+template <>
+class MEDIA_EXPORT CdmPromiseTemplate<KeyIdsVector> : public CdmPromise {
+ public:
+ CdmPromiseTemplate(base::Callback<void(const KeyIdsVector&)> resolve_cb,
+ PromiseRejectedCB rejected_cb);
+ CdmPromiseTemplate(base::Callback<void(const KeyIdsVector&)> resolve_cb,
+ PromiseRejectedCB rejected_cb,
+ const std::string& uma_name);
+ virtual ~CdmPromiseTemplate();
+ virtual void resolve(const KeyIdsVector& result);
+ virtual ResolveParameterType GetResolveParameterType() const OVERRIDE;
+
+ private:
+ base::Callback<void(const KeyIdsVector&)> resolve_cb_;
DISALLOW_COPY_AND_ASSIGN(CdmPromiseTemplate);
};
FrameStats* frame_stats = GetFrameStats(last_acked_frame_);
while (IsNewerFrameId(frame_id, last_acked_frame_)) {
FrameStats* last_frame_stats = frame_stats;
- last_acked_frame_++;
- frame_stats = GetFrameStats(last_acked_frame_);
+ frame_stats = GetFrameStats(last_acked_frame_ + 1);
DCHECK(frame_stats);
+ if (frame_stats->sent_time.is_null()) {
+ // Can't ack a frame that hasn't been sent yet.
+ return;
+ }
+ last_acked_frame_++;
+ if (when < frame_stats->sent_time)
+ when = frame_stats->sent_time;
+
frame_stats->ack_time = when;
acked_bits_in_history_ += frame_stats->frame_size;
dead_time_in_history_ += DeadTime(*last_frame_stats, *frame_stats);
response_size);
}
-void CdmAdapter::ReleaseSession(uint32_t promise_id,
- const std::string& web_session_id) {
- cdm_->CloseSession(
- promise_id, web_session_id.data(), web_session_id.length());
-}
-
-void CdmAdapter::RemoveSession(uint32_t promise_id,
- const std::string& web_session_id) {
- if (!cdm_->RemoveSession(
+void CdmAdapter::CloseSession(uint32_t promise_id,
+ const std::string& web_session_id) {
+ if (!cdm_->CloseSession(
promise_id, web_session_id.data(), web_session_id.length())) {
// CDM_4 and CDM_5 don't support this method, so reject the promise.
RejectPromise(promise_id, cdm::kNotSupportedError, 0, "Not implemented.");
}
}
+void CdmAdapter::ReleaseSession(uint32_t promise_id,
+ const std::string& web_session_id) {
+ cdm_->RemoveSession(
+ promise_id, web_session_id.data(), web_session_id.length());
+}
+
void CdmAdapter::GetUsableKeyIds(uint32_t promise_id,
const std::string& web_session_id) {
if (!cdm_->GetUsableKeyIds(
virtual void UpdateSession(uint32_t promise_id,
const std::string& web_session_id,
pp::VarArrayBuffer response) OVERRIDE;
- // TODO(jrummell): Rename to CloseSession().
+ // TODO(jrummell): Pass this function through Pepper and add OVERRIDE.
+ virtual void CloseSession(uint32_t promise_id,
+ const std::string& web_session_id);
+ // TODO(jrummell): Rename to RemoveSession().
virtual void ReleaseSession(uint32_t promise_id,
const std::string& web_session_id) OVERRIDE;
- // TODO(jrummell): Pass these 2 functions through Pepper and add OVERRIDE.
- virtual void RemoveSession(uint32_t promise_id,
- const std::string& web_session_id);
+ // TODO(jrummell): Pass this function through Pepper and add OVERRIDE.
virtual void GetUsableKeyIds(uint32_t promise_id,
const std::string& web_session_id);
virtual void Decrypt(
uint32_t web_session_id_size,
const uint8_t* response,
uint32_t response_size) = 0;
- virtual void CloseSession(uint32_t promise_id,
+ // TODO(jrummell): Remove return value when CDM4/5 are removed.
+ virtual bool CloseSession(uint32_t promise_id,
const char* web_session_id,
uint32_t web_session_id_size) = 0;
- virtual bool RemoveSession(uint32_t promise_id,
+ virtual void RemoveSession(uint32_t promise_id,
const char* web_session_id,
uint32_t web_session_id_size) = 0;
+ // TODO(jrummell): Remove return value when CDM4/5 are removed.
virtual bool GetUsableKeyIds(uint32_t promise_id,
const char* web_session_id,
uint32_t web_session_id_size) = 0;
return true;
}
- virtual void CloseSession(uint32_t promise_id,
+ virtual bool CloseSession(uint32_t promise_id,
const char* web_session_id,
uint32_t web_session_id_size) OVERRIDE {
cdm_->CloseSession(promise_id, web_session_id, web_session_id_size);
+ return true;
}
- virtual bool RemoveSession(uint32_t promise_id,
+ virtual void RemoveSession(uint32_t promise_id,
const char* web_session_id,
uint32_t web_session_id_size) OVERRIDE {
cdm_->RemoveSession(promise_id, web_session_id, web_session_id_size);
- return true;
}
virtual void TimerExpired(void* context) OVERRIDE {
}
template <>
-void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::CloseSession(
+bool CdmWrapperImpl<cdm::ContentDecryptionModule_4>::CloseSession(
uint32_t promise_id,
const char* web_session_id,
uint32_t web_session_id_size) {
- std::string web_session_str(web_session_id, web_session_id_size);
- uint32_t session_id = LookupSessionId(web_session_str);
- RegisterPromise(session_id, promise_id);
- cdm_->ReleaseSession(session_id);
+ return false;
}
template <>
-bool CdmWrapperImpl<cdm::ContentDecryptionModule_4>::RemoveSession(
+void CdmWrapperImpl<cdm::ContentDecryptionModule_4>::RemoveSession(
uint32_t promise_id,
const char* web_session_id,
uint32_t web_session_id_size) {
- return false;
+ std::string web_session_str(web_session_id, web_session_id_size);
+ uint32_t session_id = LookupSessionId(web_session_str);
+ RegisterPromise(session_id, promise_id);
+ cdm_->ReleaseSession(session_id);
}
template <>
}
template <>
-void CdmWrapperImpl<cdm::ContentDecryptionModule_5>::CloseSession(
+bool CdmWrapperImpl<cdm::ContentDecryptionModule_5>::CloseSession(
uint32_t promise_id,
const char* web_session_id,
uint32_t web_session_id_size) {
- cdm_->ReleaseSession(promise_id, web_session_id, web_session_id_size);
+ return false;
}
template <>
-bool CdmWrapperImpl<cdm::ContentDecryptionModule_5>::RemoveSession(
+void CdmWrapperImpl<cdm::ContentDecryptionModule_5>::RemoveSession(
uint32_t promise_id,
const char* web_session_id,
uint32_t web_session_id_size) {
- return false;
+ cdm_->ReleaseSession(promise_id, web_session_id, web_session_id_size);
}
template <>
start_time = base::TimeDelta();
}
+ // Don't rebase timestamps for positive start times, the HTML Media Spec
+ // details this in section "4.8.10.6 Offsets into the media resource." We
+ // will still need to rebase timestamps before seeking with FFmpeg though.
+ if (start_time > base::TimeDelta())
+ start_time = base::TimeDelta();
+
buffer->set_timestamp(stream_timestamp - start_time);
// If enabled, mark audio packets with negative timestamps for post-decode
void FFmpegDemuxer::Stop(const base::Closure& callback) {
DCHECK(task_runner_->BelongsToCurrentThread());
- url_protocol_->Abort();
+
+ // The order of Stop() and Abort() is important here. If Abort() is called
+ // first, control may pass into FFmpeg where it can destruct buffers that are
+ // in the process of being fulfilled by the DataSource.
data_source_->Stop();
+ url_protocol_->Abort();
// This will block until all tasks complete. Note that after this returns it's
// possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this
// we know we're going to drop it on the floor.
// FFmpeg requires seeks to be adjusted according to the lowest starting time.
- const base::TimeDelta seek_time = time + start_time_;
+ // Since EnqueuePacket() rebased negative timestamps by the start time, we
+ // must correct the shift here.
+ //
+ // Additionally, to workaround limitations in how we expose seekable ranges to
+ // Blink (http://crbug.com/137275), we also want to clamp seeks before the
+ // start time to the start time.
+ const base::TimeDelta seek_time =
+ start_time_ < base::TimeDelta() ? time + start_time_
+ : time < start_time_ ? start_time_ : time;
// Choose the seeking stream based on whether it contains the seek time, if no
// match can be found prefer the preferred stream.
// Run the test twice with a seek in between.
for (int i = 0; i < 2; ++i) {
- // Check first buffer in video stream. It should have been adjusted such
- // that it starts 400ms after the first audio buffer.
- video->Read(
- NewReadCB(FROM_HERE,
- 5636,
- (video_start_time - audio_start_time).InMicroseconds()));
+ video->Read(NewReadCB(FROM_HERE, 5636, video_start_time.InMicroseconds()));
message_loop_.Run();
-
- // Since the audio buffer has a lower first timestamp, it should become
- // zero.
- audio->Read(NewReadCB(FROM_HERE, 165, 0));
+ audio->Read(NewReadCB(FROM_HERE, 165, audio_start_time.InMicroseconds()));
message_loop_.Run();
// Verify that the start time is equal to the lowest timestamp (ie the
}
const PictureBuffer& pb = it->second;
+ // Validate picture rectangle from GPU. This is for sanity/security check
+ // even the rectangle is not used in this class.
+ if (picture.visible_rect().IsEmpty() ||
+ !gfx::Rect(pb.size()).Contains(picture.visible_rect())) {
+ NOTREACHED() << "Invalid picture size from VDA: "
+ << picture.visible_rect().ToString() << " should fit in "
+ << pb.size().ToString();
+ NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
+ return;
+ }
+
// Update frame's timestamp.
base::TimeDelta timestamp;
+ // Some of the VDAs don't support and thus don't provide us with visible
+ // size in picture.size, passing coded size instead, so always drop it and
+ // use config information instead.
gfx::Rect visible_rect;
gfx::Size natural_size;
GetBufferData(picture.bitstream_buffer_id(), ×tamp, &visible_rect,
SkCanvas* canvas,
const gfx::RectF& dest_rect,
uint8 alpha,
+ SkXfermode::Mode mode,
VideoRotation video_rotation) {
if (alpha == 0) {
return;
last_frame_timestamp_ = video_frame->timestamp();
}
- // Use SRC mode so we completely overwrite the buffer (in case we have alpha)
- // this means we don't need the extra cost of clearing the buffer first.
- paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode));
+ paint.setXfermodeMode(mode);
// Paint using |last_frame_|.
paint.setFilterLevel(SkPaint::kLow_FilterLevel);
canvas->drawBitmapRect(last_frame_, NULL, dest, &paint);
}
+void SkCanvasVideoRenderer::Copy(media::VideoFrame* video_frame,
+ SkCanvas* canvas) {
+ Paint(video_frame,
+ canvas,
+ video_frame->visible_rect(),
+ 0xff,
+ SkXfermode::kSrc_Mode,
+ media::VIDEO_ROTATION_0);
+}
+
} // namespace media
#include "media/base/media_export.h"
#include "media/base/video_rotation.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkXfermode.h"
#include "ui/gfx/rect.h"
class SkCanvas;
SkCanvas* canvas,
const gfx::RectF& dest_rect,
uint8 alpha,
+ SkXfermode::Mode mode,
VideoRotation video_rotation);
+ // Copy |video_frame| on |canvas|.
+ void Copy(media::VideoFrame* video_frame, SkCanvas* canvas);
+
private:
// An RGB bitmap and corresponding timestamp of the previously converted
// video frame data.
void PaintRotated(VideoFrame* video_frame,
SkCanvas* canvas,
Color color,
+ SkXfermode::Mode mode,
VideoRotation video_rotation);
+ void Copy(VideoFrame* video_frame, SkCanvas* canvas);
+
// Getters for various frame sizes.
VideoFrame* natural_frame() { return natural_frame_.get(); }
VideoFrame* larger_frame() { return larger_frame_.get(); }
SkCanvasVideoRendererTest::~SkCanvasVideoRendererTest() {}
void SkCanvasVideoRendererTest::PaintWithoutFrame(SkCanvas* canvas) {
- renderer_.Paint(NULL, canvas, kNaturalRect, 0xFF, VIDEO_ROTATION_0);
+ renderer_.Paint(NULL,
+ canvas,
+ kNaturalRect,
+ 0xFF,
+ SkXfermode::kSrcOver_Mode,
+ VIDEO_ROTATION_0);
}
void SkCanvasVideoRendererTest::Paint(VideoFrame* video_frame,
SkCanvas* canvas,
Color color) {
- PaintRotated(video_frame, canvas, color, VIDEO_ROTATION_0);
+ PaintRotated(
+ video_frame, canvas, color, SkXfermode::kSrcOver_Mode, VIDEO_ROTATION_0);
}
void SkCanvasVideoRendererTest::PaintRotated(VideoFrame* video_frame,
SkCanvas* canvas,
Color color,
+ SkXfermode::Mode mode,
VideoRotation video_rotation) {
switch (color) {
case kNone:
media::FillYUV(video_frame, 29, 255, 107);
break;
}
- renderer_.Paint(video_frame, canvas, kNaturalRect, 0xFF, video_rotation);
+ renderer_.Paint(
+ video_frame, canvas, kNaturalRect, 0xFF, mode, video_rotation);
+}
+
+void SkCanvasVideoRendererTest::Copy(VideoFrame* video_frame,
+ SkCanvas* canvas) {
+ renderer_.Copy(video_frame, canvas);
}
TEST_F(SkCanvasVideoRendererTest, NoFrame) {
}
TEST_F(SkCanvasVideoRendererTest, TransparentFrame) {
- // Test that we don't blend with existing canvas contents.
FillCanvas(target_canvas(), SK_ColorRED);
- Paint(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)),
- target_canvas(),
- kNone);
+ PaintRotated(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)),
+ target_canvas(),
+ kNone,
+ SkXfermode::kSrcOver_Mode,
+ VIDEO_ROTATION_0);
+ EXPECT_EQ(static_cast<SkColor>(SK_ColorRED), GetColor(target_canvas()));
+}
+
+TEST_F(SkCanvasVideoRendererTest, TransparentFrameSrcMode) {
+ FillCanvas(target_canvas(), SK_ColorRED);
+ // SRC mode completely overwrites the buffer.
+ PaintRotated(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)),
+ target_canvas(),
+ kNone,
+ SkXfermode::kSrc_Mode,
+ VIDEO_ROTATION_0);
+ EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
+ GetColor(target_canvas()));
+}
+
+TEST_F(SkCanvasVideoRendererTest, CopyTransparentFrame) {
+ FillCanvas(target_canvas(), SK_ColorRED);
+ Copy(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)),
+ target_canvas());
EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
GetColor(target_canvas()));
}
TEST_F(SkCanvasVideoRendererTest, Video_Rotation_90) {
SkCanvas canvas(AllocBitmap(kWidth, kHeight));
const gfx::Rect crop_rect = cropped_frame()->visible_rect();
- PaintRotated(cropped_frame(), &canvas, kNone, VIDEO_ROTATION_90);
+ PaintRotated(cropped_frame(),
+ &canvas,
+ kNone,
+ SkXfermode::kSrcOver_Mode,
+ VIDEO_ROTATION_90);
// Check the corners.
EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, 0, 0));
EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, 0));
TEST_F(SkCanvasVideoRendererTest, Video_Rotation_180) {
SkCanvas canvas(AllocBitmap(kWidth, kHeight));
const gfx::Rect crop_rect = cropped_frame()->visible_rect();
- PaintRotated(cropped_frame(), &canvas, kNone, VIDEO_ROTATION_180);
+ PaintRotated(cropped_frame(),
+ &canvas,
+ kNone,
+ SkXfermode::kSrcOver_Mode,
+ VIDEO_ROTATION_180);
// Check the corners.
EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, 0, 0));
EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, 0));
TEST_F(SkCanvasVideoRendererTest, Video_Rotation_270) {
SkCanvas canvas(AllocBitmap(kWidth, kHeight));
const gfx::Rect crop_rect = cropped_frame()->visible_rect();
- PaintRotated(cropped_frame(), &canvas, kNone, VIDEO_ROTATION_270);
+ PaintRotated(cropped_frame(),
+ &canvas,
+ kNone,
+ SkXfermode::kSrcOver_Mode,
+ VIDEO_ROTATION_270);
// Check the corners.
EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, 0, 0));
EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth - 1, 0));
texture_mailbox_(texture_mailbox) {
}
-Picture::Picture(int32 picture_buffer_id, int32 bitstream_buffer_id)
+Picture::Picture(int32 picture_buffer_id,
+ int32 bitstream_buffer_id,
+ const gfx::Rect& visible_rect)
: picture_buffer_id_(picture_buffer_id),
- bitstream_buffer_id_(bitstream_buffer_id) {
+ bitstream_buffer_id_(bitstream_buffer_id),
+ visible_rect_(visible_rect) {
}
} // namespace media
#include "base/basictypes.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "media/base/media_export.h"
+#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/size.h"
namespace media {
// This is the media-namespace equivalent of PP_Picture_Dev.
class MEDIA_EXPORT Picture {
public:
- Picture(int32 picture_buffer_id, int32 bitstream_buffer_id);
+ Picture(int32 picture_buffer_id,
+ int32 bitstream_buffer_id,
+ const gfx::Rect& visible_rect);
// Returns the id of the picture buffer where this picture is contained.
int32 picture_buffer_id() const {
bitstream_buffer_id_ = bitstream_buffer_id;
}
+ // Returns the visible rectangle of the picture. Its size may be smaller
+ // than the size of the PictureBuffer, as it is the only visible part of the
+ // Picture contained in the PictureBuffer.
+ gfx::Rect visible_rect() const { return visible_rect_; }
+
private:
int32 picture_buffer_id_;
int32 bitstream_buffer_id_;
+ gfx::Rect visible_rect_;
};
} // namespace media
nacl_host_desc_big_file_nexe = env.ComponentProgram(
'nacl_host_desc_big_file_test',
['nacl_host_desc_big_file_test.c'],
- EXTRA_LIBS=['platform'])
+ EXTRA_LIBS=['platform', 'nrd_xfer'])
d = os.path.join(str(env.Dir('${TARGET_ROOT}')), 'large_temporary_files')
try:
#include "native_client/src/shared/platform/nacl_sync_checked.h"
#include "native_client/src/shared/platform/win/xlate_system_error.h"
#include "native_client/src/trusted/desc/nacl_desc_effector.h"
+#include "native_client/src/trusted/desc/nacl_desc_effector_trusted_mem.h"
#include "native_client/src/trusted/service_runtime/nacl_config.h"
#include "native_client/src/trusted/service_runtime/internal_errno.h"
", addr + chunk_offset %"NACL_PRIxPTR"\n",
map_result, chunk_addr, (addr + chunk_offset));
if ((addr + chunk_offset) != map_result) {
+ /*
+ * MapViewOfFileEx() failed. If we are mapping into untrusted
+ * address space, we opened an mmap hole. We didn't expect the
+ * failure, and it's difficult to restore the old mappings that we
+ * removed, so for safety we must abort with LOG_FATAL.
+ *
+ * Otherwise, if this is a trusted mapping, we can return an error
+ * gracefully. NaClElfFileMapSegment() currently triggers errors
+ * here by mapping beyond the file's extent: see
+ * https://crbug.com/406632.
+ */
+ int log_type =
+ effp == NaClDescEffectorTrustedMem() ? LOG_ERROR : LOG_FATAL;
DWORD err = GetLastError();
- NaClLog(LOG_FATAL,
+ size_t unmap_offset;
+ NaClLog(log_type,
"MapViewOfFileEx failed at 0x%08"NACL_PRIxPTR
", got 0x%08"NACL_PRIxPTR", err %x\n",
addr + chunk_offset,
map_result,
err);
+ for (unmap_offset = 0;
+ unmap_offset < chunk_offset;
+ unmap_offset += NACL_MAP_PAGESIZE) {
+ (void) UnmapViewOfFile((void *) (addr + unmap_offset));
+ }
+ retval = (uintptr_t) -NaClXlateSystemError(err);
+ goto cleanup;
}
if (!VirtualProtect((void *) map_result,
NaClRoundPage(chunk_size),
env.ComponentProgram(
'ncval_stubout',
'ncstubout.c',
- EXTRA_LIBS=['validators', env.NaClTargetArchSuffix('ncvalidate_verbose')])
+ EXTRA_LIBS=['validators', env.NaClTargetArchSuffix('ncvalidate_verbose'),
+ 'nrd_xfer'])
#include "gtest/gtest.h"
#include "native_client/src/include/portability_io.h"
+#include "native_client/src/trusted/desc/nacl_desc_effector_trusted_mem.h"
#include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
#include "native_client/src/trusted/desc/nacl_desc_io.h"
#include "native_client/src/trusted/desc/nrd_all_modules.h"
#include "native_client/src/trusted/service_runtime/include/bits/mman.h"
+#include "native_client/src/trusted/service_runtime/include/sys/errno.h"
#include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
#include "native_client/src/trusted/service_runtime/mmap_test_check.h"
#include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
#include "native_client/src/trusted/service_runtime/sel_ldr.h"
#include "native_client/src/trusted/service_runtime/sys_memory.h"
+static const int kTestFillByte = 0x42;
+
class MmapTest : public testing::Test {
protected:
virtual void SetUp();
NaClNrdAllModulesFini();
}
+// Creates a temporary file and returns an FD for it.
+static int MakeTempFileFd() {
+ int host_fd;
+#if NACL_WINDOWS
+ // Open temporary file that is deleted automatically.
+ const char *temp_prefix = "nacl_mmap_test_temp_";
+ char *temp_filename = _tempnam("C:\\Windows\\Temp", temp_prefix);
+ EXPECT_EQ(_sopen_s(&host_fd, temp_filename,
+ _O_RDWR | _O_CREAT | _O_TEMPORARY,
+ _SH_DENYNO, _S_IREAD | _S_IWRITE), 0);
+#else
+ char temp_filename[] = "/tmp/nacl_mmap_test_temp_XXXXXX";
+ host_fd = mkstemp(temp_filename);
+ EXPECT_GE(host_fd, 0);
+#endif
+ EXPECT_EQ(remove(temp_filename), 0);
+ return host_fd;
+}
+
+// Creates a temporary file of the given size and returns a NaClDesc for it.
+static struct NaClDesc *MakeTempFileNaClDesc(size_t file_size) {
+ int host_fd = MakeTempFileFd();
+
+ struct NaClHostDesc *host_desc =
+ (struct NaClHostDesc *) malloc(sizeof(*host_desc));
+ EXPECT_TRUE(host_desc);
+ EXPECT_EQ(NaClHostDescPosixTake(host_desc, host_fd, NACL_ABI_O_RDWR), 0);
+ struct NaClDesc *desc = (struct NaClDesc *) NaClDescIoDescMake(host_desc);
+
+ // Fill the file. On Windows, this is necessary to ensure that NaCl
+ // gives us mappings from the file instead of PROT_NONE-fill.
+ char *buf = new char[file_size];
+ memset(buf, kTestFillByte, file_size);
+ ssize_t written = NACL_VTBL(NaClDesc, desc)->Write(desc, buf, file_size);
+ EXPECT_EQ(written, (ssize_t) file_size);
+ delete[] buf;
+
+ return desc;
+}
+
// These tests are disabled for ARM/MIPS because the ARM/MIPS sandboxes are
// zero-based, and sel_addrspace_(arm/mips).c do not work when allocating a
// non-zero-based region.
}
void MapFileFd(struct NaClApp *nap, uintptr_t addr, size_t file_size) {
- int host_fd;
-#if NACL_WINDOWS
- // Open temporary file that is deleted automatically.
- const char *temp_prefix = "nacl_mmap_test_temp_";
- char *temp_filename = _tempnam("C:\\Windows\\Temp", temp_prefix);
- ASSERT_EQ(_sopen_s(&host_fd, temp_filename,
- _O_RDWR | _O_CREAT | _O_TEMPORARY,
- _SH_DENYNO, _S_IREAD | _S_IWRITE), 0);
-#else
- char temp_filename[] = "/tmp/nacl_mmap_test_temp_XXXXXX";
- host_fd = mkstemp(temp_filename);
- ASSERT_GE(host_fd, 0);
-#endif
- ASSERT_EQ(remove(temp_filename), 0);
-
- struct NaClHostDesc *host_desc =
- (struct NaClHostDesc *) malloc(sizeof(*host_desc));
- ASSERT_TRUE(host_desc);
- ASSERT_EQ(NaClHostDescPosixTake(host_desc, host_fd, NACL_ABI_O_RDWR), 0);
- struct NaClDesc *desc = (struct NaClDesc *) NaClDescIoDescMake(host_desc);
-
- // Fill the file. On Windows, this is necessary to ensure that NaCl
- // gives us mappings from the file instead of PROT_NONE-fill.
- char *buf = new char[file_size];
- memset(buf, 0, file_size);
- ssize_t written = NACL_VTBL(NaClDesc, desc)->Write(desc, buf, file_size);
- ASSERT_EQ(written, (ssize_t) file_size);
- delete[] buf;
-
+ struct NaClDesc *desc = MakeTempFileNaClDesc(file_size);
int fd = NaClAppSetDescAvail(nap, desc);
uintptr_t mapping_addr = (uint32_t) NaClSysMmapIntern(
}
#endif /* NACL_ARCH(NACL_BUILD_ARCH) != NACL_arm */
+
+static void AssertArrayFilled(const char *array, size_t size) {
+ for (size_t i = 0; i < size; ++i)
+ EXPECT_EQ(array[i], kTestFillByte);
+}
+
+TEST_F(MmapTest, TestTrustedMapBeyondFileExtent) {
+ int file_size = 0x10099;
+ int file_size_rounded_up = 0x20000;
+ int file_offset = 0;
+ struct NaClDesc *desc = MakeTempFileNaClDesc(file_size);
+ uintptr_t map_result;
+
+ // Map() should work with a non-page-aligned size.
+ map_result = NACL_VTBL(NaClDesc, desc)->Map(
+ desc,
+ NaClDescEffectorTrustedMem(),
+ (void *) NULL,
+ file_size,
+ NACL_ABI_PROT_READ,
+ NACL_ABI_MAP_PRIVATE,
+ file_offset);
+ ASSERT_FALSE(NaClPtrIsNegErrno(&map_result));
+ AssertArrayFilled((char *) map_result, file_size);
+ NaClDescUnmapUnsafe(desc, (void *) map_result, file_size);
+
+ // Check the behaviour of Map() when given a rounded-up (page-aligned)
+ // size that goes beyond the file's extent.
+ map_result = NACL_VTBL(NaClDesc, desc)->Map(
+ desc,
+ NaClDescEffectorTrustedMem(),
+ (void *) NULL,
+ file_size_rounded_up,
+ NACL_ABI_PROT_READ,
+ NACL_ABI_MAP_PRIVATE,
+ file_offset);
+ if (NACL_WINDOWS) {
+ // On Windows, using a size beyond the file's extent is not allowed,
+ // even within a page, so we expect an error.
+ //
+ // This tests that we get a graceful error rather than a LOG_FATAL.
+ // This is a partial regression test for https://crbug.com/406632:
+ // NaClElfFileMapSegment() currently depends on the error being
+ // graceful.
+ ASSERT_TRUE(NaClPtrIsNegErrno(&map_result));
+ ASSERT_EQ(-(intptr_t) map_result, NACL_ABI_EACCES);
+ } else {
+ // On Unix, the Map() should succeed.
+ ASSERT_FALSE(NaClPtrIsNegErrno(&map_result));
+ AssertArrayFilled((char *) map_result, file_size);
+ NaClDescUnmapUnsafe(desc, (void *) map_result, file_size_rounded_up);
+ }
+}
'validators',
env.NaClTargetArchSuffix('ncvalidate'),
env.NaClTargetArchSuffix('dfa_validate_caller'),
- 'platform', 'gio', 'utils',
+ 'platform', 'nrd_xfer', 'gio', 'utils',
])
# NOTE: can't use stdout_golden here because the test runs too long,
Native Client Tools Bundle
Version: ${VERSION}
Chrome Revision: ${CHROME_REVISION}
+Chrome Commit Position: ${CHROME_COMMIT_POSITION}
Native Client Revision: ${NACL_REVISION}
Build Date: ${DATE}
readme_text = open(os.path.join(SDK_SRC_DIR, 'README')).read()
readme_text = readme_text.replace('${VERSION}', pepper_ver)
readme_text = readme_text.replace('${CHROME_REVISION}', chrome_revision)
+ readme_text = readme_text.replace('${CHROME_COMMIT_POSITION}',
+ build_version.ChromeCommitPosition())
readme_text = readme_text.replace('${NACL_REVISION}', nacl_revision)
# Year/Month/Day Hour:Minute:Second
"""
import os
+import re
import sys
# pylint: disable=E0602
Returns:
Chrome version string or trunk + svn rev.
'''
- info = lastchange.FetchVersionInfo(None)
- if info.url.startswith('/trunk/'):
- return 'trunk.%s' % info.revision
- else:
- return ChromeVersionNoTrunk()
+ info = FetchGitCommitPosition()
+ if info.url == 'git':
+ _, ref, revision = ParseCommitPosition(info.revision)
+ if ref == 'refs/heads/master':
+ return 'trunk.%s' % revision
+ return ChromeVersionNoTrunk()
def ChromeVersionNoTrunk():
def ChromeRevision():
'''Extract chrome revision from svn.
+ Now that the Chrome source-of-truth is git, this will return the
+ Cr-Commit-Position instead. Fortunately, this value is equal to the SVN
+ revision if one exists.
+
Returns:
The Chrome revision as a string. e.g. "12345"
'''
- return lastchange.FetchVersionInfo(None).revision
+ version = FetchGitCommitPosition()
+ return ParseCommitPosition(version.revision)[2]
+
+
+def ChromeCommitPosition():
+ '''Return the full git sha and commit position.
+
+ Returns:
+ A value like:
+ 0178d4831bd36b5fb9ff477f03dc43b11626a6dc-refs/heads/master@{#292238}
+ '''
+ return FetchGitCommitPosition().revision
+
def NaClRevision():
'''Extract NaCl revision from svn.
The NaCl revision as a string. e.g. "12345"
'''
nacl_dir = os.path.join(SRC_DIR, 'native_client')
- return lastchange.FetchVersionInfo(None, nacl_dir).revision
+ return lastchange.FetchVersionInfo(None, nacl_dir, 'native_client').revision
+
+
+def FetchGitCommitPosition(directory=None):
+ '''Return the "commit-position" of the Chromium git repo. This should be
+ equivalent to the SVN revision if one exists.
+ '''
+ SEARCH_LIMIT = 100
+ for i in xrange(SEARCH_LIMIT):
+ cmd = ['show', '-s', '--format=%H%n%B', 'HEAD~%d' % i]
+ proc = lastchange.RunGitCommand(directory, cmd)
+ if not proc:
+ break
+
+ output = proc.communicate()[0]
+ if not (proc.returncode == 0 and output):
+ break
+
+ lines = output.splitlines()
+
+ # First line is the hash.
+ hsh = lines[0]
+ if not re.match(r'[0-9a-fA-F]+', hsh):
+ break
+
+ for line in reversed(lines):
+ if line.startswith('Cr-Commit-Position:'):
+ pos = line.rsplit()[-1].strip()
+ return lastchange.VersionInfo('git', '%s-%s' % (hsh, pos))
+
+ raise Exception('Unable to fetch a Git Commit Position.')
+
+
+
+def ParseCommitPosition(commit_position):
+ '''Parse a Chrome commit position into its components.
+
+ Given a commit position like:
+ 0178d4831bd36b5fb9ff477f03dc43b11626a6dc-refs/heads/master@{#292238}
+ Returns:
+ ("0178d4831bd36b5fb9ff477f03dc43b11626a6dc", "refs/heads/master", "292238")
+ '''
+ m = re.match(r'([0-9a-fA-F]+)(?:-([^@]+)@{#(\d+)})?', commit_position)
+ if m:
+ return m.groups()
+ return None
--- /dev/null
+#!/usr/bin/env python
+# Copyright (c) 2014 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.
+
+import os
+import sys
+import collections
+import unittest
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+BUILD_TOOLS_DIR = os.path.dirname(SCRIPT_DIR)
+CHROME_SRC = os.path.dirname(os.path.dirname(os.path.dirname(BUILD_TOOLS_DIR)))
+MOCK_DIR = os.path.join(CHROME_SRC, "third_party", "pymock")
+
+# For the mock library
+sys.path.append(MOCK_DIR)
+import mock
+
+sys.path.append(BUILD_TOOLS_DIR)
+import build_version
+
+ProcInfo = collections.namedtuple('ProcInfo', ['returncode', 'output'])
+
+class TestCase(unittest.TestCase):
+ def setUp(self):
+ self.fetch_svn = mock.patch('lastchange.FetchSVNRevision').start()
+ self.fetch_git_svn = mock.patch('lastchange.FetchGitSVNRevision').start()
+ self.run_git = mock.patch('lastchange.RunGitCommand').start()
+
+ self.fetch_svn.return_value = None
+ self.fetch_git_svn.return_value = None
+
+ def tearDown(self):
+ mock.patch.stopall()
+
+ def mockGitCommand(self, *args):
+ side_effects = []
+ for proc_info in args:
+ mock_proc = mock.MagicMock()
+ mock_proc.returncode = proc_info.returncode
+ comm_result = mock_proc.MagicMock()
+ comm_result.__getitem__.return_value = proc_info.output
+ mock_proc.communicate.return_value = comm_result
+ side_effects.append(mock_proc)
+
+ self.run_git.side_effect = side_effects
+
+ def mockDefaultGitCommand(self):
+ output = """\
+6a8b61d6be4656e682eba005a1dd7f129789129c
+[NaCl SDK] Update build_sdk.py to display Cr-Commit-Position in README.
+
+BUG=none
+R=bradnelson@google.com, bradnelson@chromium.org
+
+Review URL: https://codereview.chromium.org/495423010
+
+Cr-Commit-Position: refs/heads/master@{#292480}"""
+ self.mockGitCommand(ProcInfo(0, output))
+
+ def mockDepthTwoGitCommand(self):
+ output0 = """\
+ae4b444a0aa09a1fa73e59b180d7d957b9a36bf2
+."""
+
+ output1 = """\
+6a8b61d6be4656e682eba005a1dd7f129789129c
+[NaCl SDK] Update build_sdk.py to display Cr-Commit-Position in README.
+
+BUG=none
+R=bradnelson@google.com, bradnelson@chromium.org
+
+Review URL: https://codereview.chromium.org/495423010
+
+Cr-Commit-Position: refs/heads/master@{#292480}"""
+ self.mockGitCommand(ProcInfo(0, output0), ProcInfo(0, output1))
+
+
+ def assertGitShowCalled(self, depth=0):
+ cmd = ['show', '-s', '--format=%H%n%B', 'HEAD~%d' % depth]
+ self.run_git.assert_called_with(None, cmd)
+
+ def testChromeVersion(self):
+ self.mockDefaultGitCommand()
+ result = build_version.ChromeVersion()
+ self.assertGitShowCalled()
+ self.assertEqual(result, 'trunk.292480')
+
+ def testChromeRevision(self):
+ self.mockDefaultGitCommand()
+ result = build_version.ChromeRevision()
+ self.assertGitShowCalled()
+ self.assertEqual(result, '292480')
+
+ def testChromeCommitPosition(self):
+ self.mockDefaultGitCommand()
+ result = build_version.ChromeCommitPosition()
+ self.assertGitShowCalled()
+ self.assertEqual(
+ result,
+ '6a8b61d6be4656e682eba005a1dd7f129789129c-refs/heads/master@{#292480}')
+
+ def testChromeCommitPositionDepthTwo(self):
+ self.mockDepthTwoGitCommand()
+ result = build_version.ChromeCommitPosition()
+ self.assertEqual(
+ result,
+ '6a8b61d6be4656e682eba005a1dd7f129789129c-refs/heads/master@{#292480}')
+
+
+if __name__ == '__main__':
+ unittest.main()
TOOLCHAIN_OUT = os.path.join(build_paths.OUT_DIR, 'sdk_tests', 'toolchain')
TEST_MODULES = [
+ 'build_version_test',
'create_html_test',
'create_nmf_test',
'easy_template_test',
version = None
revision = None
+ commit_position = None
for line in open(readme):
if ':' in line:
name, value = line.split(':', 1)
version = value.strip()
if name == "Chrome Revision":
revision = value.strip()
+ if name == "Chrome Commit Position":
+ commit_position = value.strip()
- if revision == None or version == None:
+ if revision is None or version is None or commit_position is None:
raise Error("error parsing SDK README: %s" % readme)
try:
except ValueError:
raise Error("error parsing SDK README: %s" % readme)
- return (version, revision)
+ return (version, revision, commit_position)
def GetSystemArch(platform):
help='Print major version of the NaCl SDK.')
parser.add_option('--sdk-revision', action='store_true',
help='Print revision number of the NaCl SDK.')
+ parser.add_option('--sdk-commit-position', action='store_true',
+ help='Print commit position of the NaCl SDK.')
parser.add_option('--check-version',
help='Check that the SDK version is at least as great as the '
'version passed in.')
out = GetSDKVersion()[0]
elif options.sdk_revision:
out = GetSDKVersion()[1]
+ elif options.sdk_commit_position:
+ out = GetSDKVersion()[2]
elif options.check_version:
required_version = ParseVersion(options.check_version)
version = GetSDKVersion()
def testGetSDKVersion(self):
"""correctly parses README to find SDK version."""
- expected_version = (16, 196)
+ expected_version = (16, 196, 'f00baacabba6e-refs/heads/master@{#100}')
with open(os.path.join(self.tempdir, 'README'), 'w') as out:
out.write('Version: %s\n' % expected_version[0])
out.write('Chrome Revision: %s\n' % expected_version[1])
+ out.write('Chrome Commit Position: %s\n' % expected_version[2])
version = getos.GetSDKVersion()
self.assertEqual(version, expected_version)
case MimeUtil::MPEG4_AAC_LC:
case MimeUtil::MPEG4_AAC_SBRv1:
case MimeUtil::H264_BASELINE:
+ case MimeUtil::H264_MAIN:
+ case MimeUtil::H264_HIGH:
case MimeUtil::VP8:
case MimeUtil::VORBIS:
return true;
// MPEG-2 variants of AAC are not supported on Android.
return false;
- case MimeUtil::H264_MAIN:
- case MimeUtil::H264_HIGH:
- // H.264 Main and High profiles are not supported on Android.
- return false;
-
case MimeUtil::VP9:
// VP9 is supported only in KitKat+ (API Level 19).
return base::android::BuildInfo::GetInstance()->sdk_int() >= 19;
void NetworkDelegate::NotifyProxyFallback(
const ProxyServer& bad_proxy,
- int net_error,
- bool did_fallback) {
+ int net_error) {
DCHECK(CalledOnValidThread());
- OnProxyFallback(bad_proxy, net_error, did_fallback);
+ OnProxyFallback(bad_proxy, net_error);
}
int NetworkDelegate::NotifyBeforeSendHeaders(
}
void NetworkDelegate::OnProxyFallback(const ProxyServer& bad_proxy,
- int net_error,
- bool did_fallback) {
+ int net_error) {
}
int NetworkDelegate::OnBeforeSendHeaders(URLRequest* request,
const ProxyService& proxy_service,
ProxyInfo* result);
void NotifyProxyFallback(const ProxyServer& bad_proxy,
- int net_error,
- bool did_fallback);
+ int net_error);
int NotifyBeforeSendHeaders(URLRequest* request,
const CompletionCallback& callback,
HttpRequestHeaders* headers);
const ProxyService& proxy_service,
ProxyInfo* result);
- // Called when use of |bad_proxy| fails due to |net_error|. |did_fallback| is
- // true if the proxy service was able to fallback to another proxy
- // configuration.
+ // Called when use of |bad_proxy| fails due to |net_error|. |net_error| is
+ // the network error encountered, if any, and OK if the fallback was
+ // for a reason other than a network error (e.g. the proxy service was
+ // explicitly directed to skip a proxy).
virtual void OnProxyFallback(const ProxyServer& bad_proxy,
- int net_error,
- bool did_fallback);
+ int net_error);
// Called right before the HTTP headers are sent. Allows the delegate to
// read/write |headers| before they get sent out. |callback| and |headers| are
0x2c, 0x50, 0xb6, 0x56, 0x3b, 0x8e, 0x2d, 0x93, 0xc3, 0x11 } },
{"1.3.6.1.4.1.6449.1.2.1.5.1", ""},
},
+ // COMODO RSA Certification Authority
+ // https://comodorsacertificationauthority-ev.comodoca.com/
+ { { { 0xaf, 0xe5, 0xd2, 0x44, 0xa8, 0xd1, 0x19, 0x42, 0x30, 0xff,
+ 0x47, 0x9f, 0xe2, 0xf8, 0x97, 0xbb, 0xcd, 0x7a, 0x8c, 0xb4 } },
+ {"1.3.6.1.4.1.6449.1.2.1.5.1", ""},
+ },
// Cybertrust Global Root
// https://evup.cybertrust.ne.jp/ctj-ev-upgrader/evseal.gif
{ { { 0x5f, 0x43, 0xe5, 0xb1, 0xbf, 0xf8, 0x78, 0x8c, 0xac, 0x1c,
0x74, 0x70, 0x19, 0x9d, 0x2a, 0xbe, 0x11, 0xe3, 0x81, 0xd1 } },
{"1.3.6.1.4.1.7879.13.24.1", "" },
},
+ // USERTrust ECC Certification Authority
+ // https://usertrustecccertificationauthority-ev.comodoca.com/
+ { { { 0xd1, 0xcb, 0xca, 0x5d, 0xb2, 0xd5, 0x2a, 0x7f, 0x69, 0x3b,
+ 0x67, 0x4d, 0xe5, 0xf0, 0x5a, 0x1d, 0x0c, 0x95, 0x7d, 0xf0 } },
+ {"1.3.6.1.4.1.6449.1.2.1.5.1", ""},
+ },
+ // USERTrust RSA Certification Authority
+ // https://usertrustrsacertificationauthority-ev.comodoca.com/
+ { { { 0x2b, 0x8f, 0x1b, 0x57, 0x33, 0x0d, 0xbb, 0xa2, 0xd0, 0x7a,
+ 0x6c, 0x51, 0xf7, 0x0e, 0xe9, 0x0d, 0xda, 0xb9, 0xad, 0x8e } },
+ {"1.3.6.1.4.1.6449.1.2.1.5.1", ""},
+ },
// UTN - DATACorp SGC
{ { { 0x58, 0x11, 0x9f, 0x0e, 0x12, 0x82, 0x87, 0xea, 0x50, 0xfd,
0xd9, 0x87, 0x45, 0x6f, 0x4f, 0x78, 0xdc, 0xfa, 0xd6, 0xd4 } },
// This test passes if it doesn't crash.
}
+TEST_F(DiskCacheEntryTest, SimpleCacheDoomCloseCreateCloseOpen) {
+ // Test sequence: Create, Doom, Close, Create, Close, Open.
+ SetSimpleCacheMode();
+ InitCache();
+
+ disk_cache::Entry* null = NULL;
+
+ const char key[] = "this is a key";
+
+ disk_cache::Entry* entry1 = NULL;
+ ASSERT_EQ(net::OK, CreateEntry(key, &entry1));
+ ScopedEntryPtr entry1_closer(entry1);
+ EXPECT_NE(null, entry1);
+
+ entry1->Doom();
+ entry1_closer.reset();
+ entry1 = NULL;
+
+ disk_cache::Entry* entry2 = NULL;
+ ASSERT_EQ(net::OK, CreateEntry(key, &entry2));
+ ScopedEntryPtr entry2_closer(entry2);
+ EXPECT_NE(null, entry2);
+
+ entry2_closer.reset();
+ entry2 = NULL;
+
+ disk_cache::Entry* entry3 = NULL;
+ ASSERT_EQ(net::OK, OpenEntry(key, &entry3));
+ ScopedEntryPtr entry3_closer(entry3);
+ EXPECT_NE(null, entry3);
+}
+
// Checks that an optimistic Create would fail later on a racing Open.
TEST_F(DiskCacheEntryTest, SimpleCacheOptimisticCreateFailsOnOpen) {
SetSimpleCacheMode();
} // namespace
+class SimpleBackendImpl::ActiveEntryProxy
+ : public SimpleEntryImpl::ActiveEntryProxy {
+ public:
+ virtual ~ActiveEntryProxy() {
+ if (backend_) {
+ DCHECK_EQ(1U, backend_->active_entries_.count(entry_hash_));
+ backend_->active_entries_.erase(entry_hash_);
+ }
+ }
+
+ static scoped_ptr<SimpleEntryImpl::ActiveEntryProxy> Create(
+ int64 entry_hash,
+ SimpleBackendImpl* backend) {
+ scoped_ptr<SimpleEntryImpl::ActiveEntryProxy>
+ proxy(new ActiveEntryProxy(entry_hash, backend));
+ return proxy.Pass();
+ }
+
+ private:
+ ActiveEntryProxy(uint64 entry_hash,
+ SimpleBackendImpl* backend)
+ : entry_hash_(entry_hash),
+ backend_(backend->AsWeakPtr()) {}
+
+ uint64 entry_hash_;
+ base::WeakPtr<SimpleBackendImpl> backend_;
+};
+
SimpleBackendImpl::SimpleBackendImpl(const FilePath& path,
int max_bytes,
net::CacheType cache_type,
return index_->max_size() / kMaxFileRatio;
}
-void SimpleBackendImpl::OnDeactivated(const SimpleEntryImpl* entry) {
- active_entries_.erase(entry->entry_hash());
-}
-
void SimpleBackendImpl::OnDoomStart(uint64 entry_hash) {
// TODO(ttuttle): Revert to DCHECK once http://crbug.com/317138 is fixed.
CHECK_EQ(0u, entries_pending_doom_.count(entry_hash));
const std::string& key) {
DCHECK_EQ(entry_hash, simple_util::GetEntryHashKey(key));
std::pair<EntryMap::iterator, bool> insert_result =
- active_entries_.insert(std::make_pair(entry_hash,
- base::WeakPtr<SimpleEntryImpl>()));
+ active_entries_.insert(EntryMap::value_type(entry_hash, NULL));
EntryMap::iterator& it = insert_result.first;
- if (insert_result.second)
- DCHECK(!it->second.get());
- if (!it->second.get()) {
- SimpleEntryImpl* entry = new SimpleEntryImpl(
- cache_type_, path_, entry_hash, entry_operations_mode_, this, net_log_);
+ const bool did_insert = insert_result.second;
+ if (did_insert) {
+ SimpleEntryImpl* entry = it->second =
+ new SimpleEntryImpl(cache_type_, path_, entry_hash,
+ entry_operations_mode_,this, net_log_);
entry->SetKey(key);
- it->second = entry->AsWeakPtr();
+ entry->SetActiveEntryProxy(ActiveEntryProxy::Create(entry_hash, this));
}
- DCHECK(it->second.get());
+ DCHECK(it->second);
// It's possible, but unlikely, that we have an entry hash collision with a
// currently active entry.
if (key != it->second->key()) {
DCHECK_EQ(0U, active_entries_.count(entry_hash));
return CreateOrFindActiveEntry(entry_hash, key);
}
- return make_scoped_refptr(it->second.get());
+ return make_scoped_refptr(it->second);
}
int SimpleBackendImpl::OpenEntryFromHash(uint64 entry_hash,
}
DCHECK(*entry);
std::pair<EntryMap::iterator, bool> insert_result =
- active_entries_.insert(std::make_pair(hash,
- base::WeakPtr<SimpleEntryImpl>()));
+ active_entries_.insert(EntryMap::value_type(hash, simple_entry));
EntryMap::iterator& it = insert_result.first;
const bool did_insert = insert_result.second;
if (did_insert) {
- // There is no active entry corresponding to this hash. The entry created
- // is put in the map of active entries and returned to the caller.
- it->second = simple_entry->AsWeakPtr();
- callback.Run(error_code);
+ // There was no active entry corresponding to this hash. We've already put
+ // the entry opened from hash in the |active_entries_|. We now provide the
+ // proxy object to the entry.
+ it->second->SetActiveEntryProxy(ActiveEntryProxy::Create(hash, this));
+ callback.Run(net::OK);
} else {
- // The entry was made active with the key while the creation from hash
- // occurred. The entry created from hash needs to be closed, and the one
- // coming from the key returned to the caller.
+ // The entry was made active while we waiting for the open from hash to
+ // finish. The entry created from hash needs to be closed, and the one
+ // in |active_entries_| can be returned to the caller.
simple_entry->Close();
it->second->OpenEntry(entry, callback);
}
// Returns the maximum file size permitted in this backend.
int GetMaxFileSize() const;
- // Removes |entry| from the |active_entries_| set, forcing future Open/Create
- // operations to construct a new object.
- void OnDeactivated(const SimpleEntryImpl* entry);
-
// Flush our SequencedWorkerPool.
static void FlushWorkerPoolForTesting();
virtual void OnExternalCacheHit(const std::string& key) OVERRIDE;
private:
- typedef base::hash_map<uint64, base::WeakPtr<SimpleEntryImpl> > EntryMap;
+ typedef base::hash_map<uint64, SimpleEntryImpl*> EntryMap;
typedef base::Callback<void(base::Time mtime, uint64 max_size, int result)>
InitializeIndexCallback;
+ class ActiveEntryProxy;
+ friend class ActiveEntryProxy;
+
// Return value of InitCacheStructureOnDisk().
struct DiskStatResult {
base::Time cache_dir_mtime;
SimpleEntryImpl* const entry_;
};
+SimpleEntryImpl::ActiveEntryProxy::~ActiveEntryProxy() {}
+
SimpleEntryImpl::SimpleEntryImpl(net::CacheType cache_type,
const FilePath& path,
const uint64 entry_hash,
CreateNetLogSimpleEntryConstructionCallback(this));
}
+void SimpleEntryImpl::SetActiveEntryProxy(
+ scoped_ptr<ActiveEntryProxy> active_entry_proxy) {
+ DCHECK(!active_entry_proxy_);
+ active_entry_proxy_.reset(active_entry_proxy.release());
+}
+
int SimpleEntryImpl::OpenEntry(Entry** out_entry,
const CompletionCallback& callback) {
DCHECK(backend_.get());
DCHECK_EQ(0U, pending_operations_.size());
DCHECK(state_ == STATE_UNINITIALIZED || state_ == STATE_FAILURE);
DCHECK(!synchronous_entry_);
- RemoveSelfFromBackend();
net_log_.EndEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY);
}
*out_entry = this;
}
-void SimpleEntryImpl::RemoveSelfFromBackend() {
- if (!backend_.get())
- return;
- backend_->OnDeactivated(this);
-}
-
void SimpleEntryImpl::MarkAsDoomed() {
doomed_ = true;
if (!backend_.get())
return;
backend_->index()->Remove(entry_hash_);
- RemoveSelfFromBackend();
+ active_entry_proxy_.reset();
}
void SimpleEntryImpl::RunNextOperationIfNeeded() {
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "net/base/cache_type.h"
#include "net/base/net_export.h"
// disk cache. It proxies for the SimpleSynchronousEntry, which performs IO
// on the worker thread.
class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry,
- public base::RefCounted<SimpleEntryImpl>,
- public base::SupportsWeakPtr<SimpleEntryImpl> {
+ public base::RefCounted<SimpleEntryImpl> {
friend class base::RefCounted<SimpleEntryImpl>;
public:
enum OperationsMode {
OPTIMISTIC_OPERATIONS,
};
+ // The Backend provides an |ActiveEntryProxy| instance to this entry when it
+ // is active, meaning it's the canonical entry for this |entry_hash_|. The
+ // entry can make itself inactive by deleting its proxy.
+ class ActiveEntryProxy {
+ public:
+ virtual ~ActiveEntryProxy() = 0;
+ };
+
SimpleEntryImpl(net::CacheType cache_type,
const base::FilePath& path,
uint64 entry_hash,
SimpleBackendImpl* backend,
net::NetLog* net_log);
+ void SetActiveEntryProxy(
+ scoped_ptr<ActiveEntryProxy> active_entry_proxy);
+
// Adds another reader/writer to this entry, if possible, returning |this| to
// |entry|.
int OpenEntry(Entry** entry, const CompletionCallback& callback);
// count.
void ReturnEntryToCaller(Entry** out_entry);
- // Ensures that |this| is no longer referenced by our |backend_|, this
- // guarantees that this entry cannot have OpenEntry/CreateEntry called again.
- void RemoveSelfFromBackend();
-
// An error occured, and the SimpleSynchronousEntry should have Doomed
// us at this point. We need to remove |this| from the Backend and the
// index.
int length,
int stream_index);
+ scoped_ptr<ActiveEntryProxy> active_entry_proxy_;
+
// All nonstatic SimpleEntryImpl methods should always be called on the IO
// thread, in all cases. |io_thread_checker_| documents and enforces this.
base::ThreadChecker io_thread_checker_;
if (result < 0)
return result;
- session_->proxy_service()->ReportSuccess(proxy_info_);
+ session_->proxy_service()->ReportSuccess(proxy_info_,
+ session_->network_delegate());
next_state_ = STATE_NONE;
return OK;
}
{7, true, "\002" "ed" "\002" "gs", true, kNoPins, DOMAIN_NOT_PINNED },
{21, true, "\017" "forewordreviews" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED },
{21, true, "\020" "giacomopelagatti" "\002" "it", true, kNoPins, DOMAIN_NOT_PINNED },
- {16, true, "\010" "globalcs" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED },
{13, true, "\010" "helichat" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED },
{23, true, "\022" "hostinginnederland" "\002" "nl", true, kNoPins, DOMAIN_NOT_PINNED },
{19, true, "\015" "isitchristmas" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED },
{ "name": "ed.gs", "include_subdomains": true, "mode": "force-https" },
{ "name": "forewordreviews.com", "include_subdomains": true, "mode": "force-https" },
{ "name": "giacomopelagatti.it", "include_subdomains": true, "mode": "force-https" },
- { "name": "globalcs.co.uk", "include_subdomains": true, "mode": "force-https" },
{ "name": "helichat.de", "include_subdomains": true, "mode": "force-https" },
{ "name": "hostinginnederland.nl", "include_subdomains": true, "mode": "force-https" },
{ "name": "isitchristmas.com", "include_subdomains": true, "mode": "force-https" },
return proxy_list_.ToPacString();
}
-bool ProxyInfo::Fallback(const BoundNetLog& net_log) {
- return proxy_list_.Fallback(&proxy_retry_info_, net_log);
+bool ProxyInfo::Fallback(int net_error, const BoundNetLog& net_log) {
+ return proxy_list_.Fallback(&proxy_retry_info_, net_error, net_log);
}
void ProxyInfo::DeprioritizeBadProxies(
// See description in ProxyList::ToPacString().
std::string ToPacString() const;
- // Marks the current proxy as bad. Returns true if there is another proxy
+ // Marks the current proxy as bad. |net_error| should contain the network
+ // error encountered when this proxy was tried, if any. If this fallback
+ // is not because of a network error, then |OK| should be passed in (eg. for
+ // reasons such as local policy). Returns true if there is another proxy is
// available to try in proxy list_.
- bool Fallback(const BoundNetLog& net_log);
+ bool Fallback(int net_error, const BoundNetLog& net_log);
// De-prioritizes the proxies that we have cached as not working, by moving
// them to the end of the proxy list.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "net/base/net_errors.h"
#include "net/proxy/proxy_info.h"
#include "testing/gtest/include/gtest/gtest.h"
EXPECT_FALSE(info.is_direct());
EXPECT_FALSE(info.is_direct_only());
// After falling back to direct, we shouldn't consider it DIRECT only.
- EXPECT_TRUE(info.Fallback(BoundNetLog()));
+ EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, BoundNetLog()));
EXPECT_TRUE(info.is_direct());
EXPECT_FALSE(info.is_direct_only());
}
}
bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info,
+ int net_error,
const BoundNetLog& net_log) {
// TODO(eroman): It would be good if instead of removing failed proxies
TimeDelta::FromMinutes(5),
true,
ProxyServer(),
+ net_error,
net_log);
// Remove this proxy from our list.
base::TimeDelta retry_delay,
bool try_while_bad,
const ProxyServer& proxy_to_retry,
+ int net_error,
const BoundNetLog& net_log) const {
// Mark this proxy as bad.
std::string proxy_key = proxy_to_retry.ToURI();
retry_info.current_delay = retry_delay;
retry_info.bad_until = TimeTicks().Now() + retry_info.current_delay;
retry_info.try_while_bad = try_while_bad;
+ retry_info.net_error = net_error;
(*proxy_retry_info)[proxy_key] = retry_info;
}
net_log.AddEvent(NetLog::TYPE_PROXY_LIST_FALLBACK,
base::TimeDelta retry_delay,
bool reconsider,
const ProxyServer& another_proxy_to_bypass,
+ int net_error,
const BoundNetLog& net_log) const {
DCHECK(retry_delay != base::TimeDelta());
}
if (!proxies_[0].is_direct()) {
- AddProxyToRetryList(proxy_retry_info, retry_delay, reconsider, proxies_[0],
+ AddProxyToRetryList(proxy_retry_info,
+ retry_delay,
+ reconsider,
+ proxies_[0],
+ net_error,
net_log);
// If an additional proxy to bypass is specified, add it to the retry map
// as well.
if (another_proxy_to_bypass.is_valid()) {
- AddProxyToRetryList(proxy_retry_info, retry_delay, reconsider,
- another_proxy_to_bypass, net_log);
+ AddProxyToRetryList(proxy_retry_info,
+ retry_delay,
+ reconsider,
+ another_proxy_to_bypass,
+ net_error,
+ net_log);
}
}
}
// Returns a serialized value for the list. The caller takes ownership of it.
base::ListValue* ToValue() const;
- // Marks the current proxy server as bad and deletes it from the list. The
- // list of known bad proxies is given by proxy_retry_info. Returns true if
- // there is another server available in the list.
+ // Marks the current proxy server as bad and deletes it from the list. The
+ // list of known bad proxies is given by |proxy_retry_info|. |net_error|
+ // should contain the network error encountered when this proxy was tried, if
+ // any. If this fallback is not because of a network error, then |OK| should
+ // be passed in (eg. for reasons such as local policy). Returns true if there
+ // is another server available in the list.
bool Fallback(ProxyRetryInfoMap* proxy_retry_info,
+ int net_error,
const BoundNetLog& net_log);
// Updates |proxy_retry_info| to indicate that the first proxy in the list
// retry after |retry_delay| if positive, and will use the default proxy retry
// duration otherwise. It may reconsider the proxy beforehand if |reconsider|
// is true. Additionally updates |proxy_retry_info| with
- // |another_proxy_to_bypass| if non-empty.
+ // |another_proxy_to_bypass| if non-empty. |net_error| should contain the
+ // network error countered when this proxy was tried, or OK if the proxy retry
+ // info is being updated for a non-network related reason (e.g. local policy).
void UpdateRetryInfoOnFallback(
ProxyRetryInfoMap* proxy_retry_info,
base::TimeDelta retry_delay,
bool reconsider,
const ProxyServer& another_proxy_to_bypass,
+ int net_error,
const BoundNetLog& net_log) const;
private:
// Updates |proxy_retry_info| to indicate that the |proxy_to_retry| in
// |proxies_| is bad for |retry_delay|, but may be reconsidered earlier if
- // |try_while_bad| is true.
+ // |try_while_bad| is true. |net_error| should contain the network error
+ // countered when this proxy was tried, or OK if the proxy retry info is
+ // being updated for a non-network related reason (e.g. local policy).
void AddProxyToRetryList(ProxyRetryInfoMap* proxy_retry_info,
base::TimeDelta retry_delay,
bool try_while_bad,
const ProxyServer& proxy_to_retry,
+ int net_error,
const BoundNetLog& net_log) const;
// List of proxies.
#include "net/proxy/proxy_list.h"
+#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/proxy/proxy_retry_info.h"
#include "net/proxy/proxy_server.h"
ProxyList list;
ProxyRetryInfoMap retry_info_map;
BoundNetLog net_log;
+ ProxyServer proxy_server(
+ ProxyServer::FromURI("foopy1:80", ProxyServer::SCHEME_HTTP));
list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
list.UpdateRetryInfoOnFallback(&retry_info_map,
base::TimeDelta::FromSeconds(60),
true,
- ProxyServer(),
+ proxy_server,
+ ERR_PROXY_CONNECTION_FAILED,
+ net_log);
+ EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy1:80"));
+ EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED,
+ retry_info_map[proxy_server.ToURI()].net_error);
+ EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80"));
+ EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy3:80"));
+ }
+ // Retrying should put the first proxy on the retry list, even if there
+ // was no network error.
+ {
+ ProxyList list;
+ ProxyRetryInfoMap retry_info_map;
+ BoundNetLog net_log;
+ ProxyServer proxy_server(
+ ProxyServer::FromURI("foopy1:80", ProxyServer::SCHEME_HTTP));
+ list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80");
+ list.UpdateRetryInfoOnFallback(&retry_info_map,
+ base::TimeDelta::FromSeconds(60),
+ true,
+ proxy_server,
+ OK,
net_log);
EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy1:80"));
+ EXPECT_EQ(OK, retry_info_map[proxy_server.ToURI()].net_error);
EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80"));
EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy3:80"));
}
base::TimeDelta::FromSeconds(60),
true,
proxy_server,
+ ERR_NAME_RESOLUTION_FAILED,
net_log);
EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy1:80"));
+ EXPECT_EQ(ERR_NAME_RESOLUTION_FAILED,
+ retry_info_map[proxy_server.ToURI()].net_error);
EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80"));
EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy3:80"));
}
base::TimeDelta::FromSeconds(60),
true,
proxy_server,
+ OK,
net_log);
EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80"));
EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy3:80"));
// True if this proxy should be considered even if still bad.
bool try_while_bad;
+
+ // The network error received when this proxy failed, or |OK| if the proxy
+ // was added to the retry list for a non-network related reason. (e.g. local
+ // policy).
+ int net_error;
};
// Map of proxy servers with the associated RetryInfo structures.
// We don't have new proxy settings to try, try to fallback to the next proxy
// in the list.
- bool did_fallback = result->Fallback(net_log);
-
- if (network_delegate) {
- network_delegate->NotifyProxyFallback(bad_proxy, net_error, did_fallback);
- }
+ bool did_fallback = result->Fallback(net_error, net_log);
// Return synchronous failure if there is nothing left to fall-back to.
// TODO(eroman): This is a yucky API, clean it up.
base::TimeDelta retry_delay,
const ProxyServer& another_bad_proxy,
const BoundNetLog& net_log) {
- result.proxy_list_.UpdateRetryInfoOnFallback(&proxy_retry_info_, retry_delay,
+ result.proxy_list_.UpdateRetryInfoOnFallback(&proxy_retry_info_,
+ retry_delay,
false,
another_bad_proxy,
+ OK,
net_log);
if (another_bad_proxy.is_valid())
return result.proxy_list_.size() > 2;
return result.proxy_list_.size() > 1;
}
-void ProxyService::ReportSuccess(const ProxyInfo& result) {
+void ProxyService::ReportSuccess(const ProxyInfo& result,
+ NetworkDelegate* network_delegate) {
DCHECK(CalledOnValidThread());
const ProxyRetryInfoMap& new_retry_info = result.proxy_retry_info();
for (ProxyRetryInfoMap::const_iterator iter = new_retry_info.begin();
iter != new_retry_info.end(); ++iter) {
ProxyRetryInfoMap::iterator existing = proxy_retry_info_.find(iter->first);
- if (existing == proxy_retry_info_.end())
+ if (existing == proxy_retry_info_.end()) {
proxy_retry_info_[iter->first] = iter->second;
+ if (network_delegate) {
+ const ProxyServer& bad_proxy =
+ ProxyServer::FromURI(iter->first, ProxyServer::SCHEME_HTTP);
+ const ProxyRetryInfo& proxy_retry_info = iter->second;
+ network_delegate->NotifyProxyFallback(bad_proxy,
+ proxy_retry_info.net_error);
+ }
+ }
else if (existing->second.bad_until < iter->second.bad_until)
existing->second.bad_until = iter->second.bad_until;
}
// and will use the default proxy retry duration otherwise. Proxies marked as
// bad will not be retried until |retry_delay| has passed. Returns true if
// there will be at least one proxy remaining in the list after fallback and
- // false otherwise.
+ // false otherwise. This method should be used to add proxies to the bad
+ // proxy list only for reasons other than a network error. If a proxy needs
+ // to be added to the bad proxy list because a network error was encountered
+ // when trying to connect to it, use |ReconsiderProxyAfterError|.
bool MarkProxiesAsBadUntil(const ProxyInfo& results,
base::TimeDelta retry_delay,
const ProxyServer& another_bad_proxy,
// Called to report that the last proxy connection succeeded. If |proxy_info|
// has a non empty proxy_retry_info map, the proxies that have been tried (and
- // failed) for this request will be marked as bad.
- void ReportSuccess(const ProxyInfo& proxy_info);
+ // failed) for this request will be marked as bad. |network_delegate| will
+ // be notified of any proxy fallbacks.
+ void ReportSuccess(const ProxyInfo& proxy_info,
+ NetworkDelegate* network_delegate);
// Call this method with a non-null |pac_request| to cancel the PAC request.
void CancelPacRequest(PacRequest* pac_request);
public:
TestProxyFallbackNetworkDelegate()
: on_proxy_fallback_called_(false),
- proxy_fallback_net_error_(0),
- proxy_did_fallback_(false) {
+ proxy_fallback_net_error_(OK) {
}
- virtual void OnProxyFallback(
- const ProxyServer& proxy_server,
- int net_error,
- bool did_fallback) OVERRIDE {
+ virtual void OnProxyFallback(const ProxyServer& proxy_server,
+ int net_error) OVERRIDE {
proxy_server_ = proxy_server;
proxy_fallback_net_error_ = net_error;
- proxy_did_fallback_ = did_fallback;
on_proxy_fallback_called_ = true;
}
return proxy_fallback_net_error_;
}
- bool proxy_did_fallback() const {
- return proxy_did_fallback_;
- }
-
private:
bool on_proxy_fallback_called_;
ProxyServer proxy_server_;
int proxy_fallback_net_error_;
- bool proxy_did_fallback_;
};
} // namespace
// Now, imagine that connecting to foopy:8080 fails: there is nothing
// left to fallback to, since our proxy list was NOT terminated by
// DIRECT.
- TestProxyFallbackNetworkDelegate network_delegate;
+ NetworkDelegate network_delegate;
TestCompletionCallback callback2;
ProxyServer expected_proxy_server = info.proxy_server();
rv = service.ReconsiderProxyAfterError(
// ReconsiderProxyAfterError returns error indicating nothing left.
EXPECT_EQ(ERR_FAILED, rv);
EXPECT_TRUE(info.is_empty());
- EXPECT_TRUE(network_delegate.on_proxy_fallback_called());
- EXPECT_EQ(expected_proxy_server, network_delegate.proxy_server());
- EXPECT_EQ(net::ERR_PROXY_CONNECTION_FAILED,
- network_delegate.proxy_fallback_net_error());
- EXPECT_FALSE(network_delegate.proxy_did_fallback());
}
// Test that if the execution of the PAC script fails (i.e. javascript runtime
EXPECT_TRUE(info.is_direct());
// Fallback 1.
- TestProxyFallbackNetworkDelegate network_delegate2;
TestCompletionCallback callback2;
rv = service.ReconsiderProxyAfterError(url, net::LOAD_NORMAL,
net::ERR_PROXY_CONNECTION_FAILED,
EXPECT_EQ(OK, rv);
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foobar:10", info.proxy_server().ToURI());
- // No network delegate provided.
- EXPECT_FALSE(network_delegate2.on_proxy_fallback_called());
// Fallback 2.
- TestProxyFallbackNetworkDelegate network_delegate3;
+ NetworkDelegate network_delegate;
ProxyServer expected_proxy_server3 = info.proxy_server();
TestCompletionCallback callback3;
rv = service.ReconsiderProxyAfterError(url, net::LOAD_NORMAL,
net::ERR_PROXY_CONNECTION_FAILED,
&info, callback3.callback(), NULL,
- &network_delegate3, BoundNetLog());
+ &network_delegate, BoundNetLog());
EXPECT_EQ(OK, rv);
EXPECT_TRUE(info.is_direct());
- EXPECT_TRUE(network_delegate3.on_proxy_fallback_called());
- EXPECT_EQ(expected_proxy_server3, network_delegate3.proxy_server());
- EXPECT_EQ(net::ERR_PROXY_CONNECTION_FAILED,
- network_delegate3.proxy_fallback_net_error());
- EXPECT_TRUE(network_delegate3.proxy_did_fallback());
// Fallback 3.
- TestProxyFallbackNetworkDelegate network_delegate4;
ProxyServer expected_proxy_server4 = info.proxy_server();
TestCompletionCallback callback4;
rv = service.ReconsiderProxyAfterError(url, net::LOAD_NORMAL,
net::ERR_PROXY_CONNECTION_FAILED,
&info, callback4.callback(), NULL,
- &network_delegate4, BoundNetLog());
+ &network_delegate, BoundNetLog());
EXPECT_EQ(OK, rv);
EXPECT_FALSE(info.is_direct());
EXPECT_EQ("foobar:20", info.proxy_server().ToURI());
- EXPECT_TRUE(network_delegate4.on_proxy_fallback_called());
- EXPECT_EQ(expected_proxy_server4, network_delegate4.proxy_server());
- EXPECT_EQ(net::ERR_PROXY_CONNECTION_FAILED,
- network_delegate4.proxy_fallback_net_error());
- EXPECT_TRUE(network_delegate4.proxy_did_fallback());
// Fallback 4 -- Nothing to fall back to!
- TestProxyFallbackNetworkDelegate network_delegate5;
ProxyServer expected_proxy_server5 = info.proxy_server();
TestCompletionCallback callback5;
rv = service.ReconsiderProxyAfterError(url, net::LOAD_NORMAL,
net::ERR_PROXY_CONNECTION_FAILED,
&info, callback5.callback(), NULL,
- &network_delegate5, BoundNetLog());
+ &network_delegate, BoundNetLog());
EXPECT_EQ(ERR_FAILED, rv);
EXPECT_TRUE(info.is_empty());
- EXPECT_TRUE(network_delegate5.on_proxy_fallback_called());
- EXPECT_EQ(expected_proxy_server5, network_delegate5.proxy_server());
- EXPECT_EQ(net::ERR_PROXY_CONNECTION_FAILED,
- network_delegate5.proxy_fallback_net_error());
- EXPECT_FALSE(network_delegate5.proxy_did_fallback());
}
TEST_F(ProxyServiceTest, PAC_ConfigSourcePropagates) {
EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
// Report back that the second proxy worked. This will globally mark the
// first proxy as bad.
- service.ReportSuccess(info);
+ TestProxyFallbackNetworkDelegate test_delegate;
+ service.ReportSuccess(info, &test_delegate);
+ EXPECT_EQ("foopy1:8080", test_delegate.proxy_server().ToURI());
+ EXPECT_EQ(net::ERR_PROXY_CONNECTION_FAILED,
+ test_delegate.proxy_fallback_net_error());
TestCompletionCallback callback3;
rv = service.ResolveProxy(
int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
HttpResponseInfo* response,
const CompletionCallback& callback) {
- CHECK(stream_);
CHECK(!request_body_stream_);
CHECK(!response_info_);
CHECK(!callback.is_null());
CHECK(response);
+ if (!stream_) {
+ return ERR_CONNECTION_CLOSED;
+ }
+
QuicPriority priority = ConvertRequestPriorityToQuicPriority(priority_);
stream_->set_priority(priority);
// Store the serialized request headers.
stream_->SetDelegate(NULL);
stream_->Reset(QUIC_STREAM_CANCELLED);
stream_ = NULL;
+ response_status_ = was_handshake_confirmed_ ?
+ ERR_CONNECTION_CLOSED : ERR_QUIC_HANDSHAKE_FAILED;
}
}
}
void QuicHttpStream::OnSessionClosed(int error) {
+ Close(false);
session_error_ = error;
session_.reset();
}
EXPECT_TRUE(AtEof());
}
+// Regression test for http://crbug.com/409101
+TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendRequest) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ Initialize();
+
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
+ net_log_, callback_.callback()));
+
+ session_->connection()->CloseConnection(QUIC_NO_ERROR, true);
+
+ EXPECT_EQ(ERR_CONNECTION_CLOSED,
+ stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+}
+
+// Regression test for http://crbug.com/409871
+TEST_P(QuicHttpStreamTest, SessionClosedBeforeReadResponseHeaders) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
+ Initialize();
+
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
+ net_log_, callback_.callback()));
+
+ EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+
+ session_->connection()->CloseConnection(QUIC_NO_ERROR, true);
+
+ EXPECT_NE(OK, stream_->ReadResponseHeaders(callback_.callback()));
+}
+
TEST_P(QuicHttpStreamTest, SendPostRequest) {
SetRequest("POST", "/", DEFAULT_PRIORITY);
AddWrite(ConstructRequestHeadersPacket(1, !kFin));
TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_);
DVLOG(4) << "Notifying output picture id " << output_id
<< " for input "<< input_id << " is ready";
+ // TODO(posciak): Use visible size from decoder here instead
+ // (crbug.com/402760).
if (client_)
- client_->PictureReady(media::Picture(output_id, input_id));
+ client_->PictureReady(media::Picture(output_id, input_id, gfx::Rect(tfp_picture->size())));
}
void VaapiVideoDecodeAccelerator::TryOutputSurface() {
}
Emf::~Emf() {
+ Close();
+}
+
+void Emf::Close() {
DCHECK(!hdc_);
if (emf_)
DeleteEnhMetaFile(emf_);
+ emf_ = NULL;
}
bool Emf::InitToFile(const base::FilePath& metafile_path) {
Emf();
virtual ~Emf();
+ // Closes metafile.
+ void Close();
+
// Generates a new metafile that will record every GDI command, and will
// be saved to |metafile_path|.
virtual bool InitToFile(const base::FilePath& metafile_path);
],
},
'dependencies': [
- 'android_support_v4_javalib_no_res',
'../base/base.gyp:base_java',
'../ui/android/ui_android.gyp:ui_java',
'remoting_android_resources',
'../third_party/android_tools/android_tools.gyp:android_support_v7_appcompat_javalib',
'../third_party/android_tools/android_tools.gyp:android_support_v7_mediarouter_javalib',
+ '../third_party/android_tools/android_tools.gyp:android_support_v13_javalib',
],
'includes': [ '../build/java.gypi' ],
'conditions' : [
},
'includes': [ '../build/java_apk.gypi' ],
}, # end of target 'remoting_test_apk'
- {
- # This jar contains the Android support v4 libary. It does not have
- # any associated resources.
- 'target_name': 'android_support_v4_javalib_no_res',
- 'type': 'none',
- 'variables': {
- 'jar_path': '../third_party/android_tools/sdk/extras/android/support/v4/android-support-v4.jar',
- },
- 'includes': ['../build/java_prebuilt.gypi'],
- }, # end of target 'android_support_v4_javalib_no_res'
], # end of 'targets'
'conditions': [
['enable_cast==1', {
* @private
*/
remoting.ClientSession.prototype.scroll_ = function(dx, dy) {
- var plugin = this.plugin_.element();
- var style = plugin.style;
-
/**
* Helper function for x- and y-scrolling
* @param {number|string} curr The current margin, eg. "10px".
return result + 'px';
};
+ var plugin = this.plugin_.element();
+ var style = this.container_.style;
+
var stopX = { stop: false };
var clientArea = this.getClientArea_();
style.marginLeft = adjustMargin(style.marginLeft, dx, clientArea.width,
};
remoting.ClientSession.prototype.resetScroll_ = function() {
- if (this.plugin_) {
- var plugin = this.plugin_.element();
- plugin.style.marginTop = '0px';
- plugin.style.marginLeft = '0px';
- }
+ this.container_.style.marginTop = '0px';
+ this.container_.style.marginLeft = '0px';
};
/**
* @return {{top: number, left:number}} The top-left corner of the plugin.
*/
remoting.ClientSession.prototype.getPluginPositionForTesting = function() {
- var plugin = this.plugin_.element();
- var style = plugin.style;
+ var style = this.container_.style;
return {
top: parseFloat(style.marginTop),
left: parseFloat(style.marginLeft)
}
private boolean isNonInvalidationType() {
- if (this == SESSION && LibraryLoader.isInitialized()) {
+ if ((this == SESSION || this == FAVICON_TRACKING) && LibraryLoader.isInitialized()) {
return FieldTrialList
.findFullName("AndroidSessionNotifications")
.equals("Disabled");
-
# This directly has manual tests that don't have to run with run-webkit-tests
crbug.com/359838 http/tests/ManualTests/ [ Skip ]
crbug.com/304953 fast/css/font-face-download-error.html [ Pass Timeout ]
+#crbug.com/411175 [ XP ] svg/custom/svg-fonts-without-missing-glyph.xhtml [ Failure Pass ]
+
# This batch were marked as NeedsRebaseline for crbug.com/133097 but are flaky:
crbug.com/310679 [ Mac ] compositing/fixed-body-background-positioned.html [ Failure Pass ]
crbug.com/310679 [ Mac ] compositing/rubberbanding/transform-overhang-e.html [ ImageOnlyFailure Pass ]
crbug.com/341435 http/tests/images/image-with-origin-header.html [ Pass Timeout ]
+Bug(vollick) compositing/overflow/nested-border-radius-clipping.html [ NeedsRebaseline ]
+Bug(vollick) virtual/gpu/compositedscrolling/overflow/nested-border-radius-clipping.html [ NeedsRebaseline ]
+
crbug.com/248938 virtual/threaded/animations/3d/transform-origin-vs-functions.html [ Pass Failure ]
crbug.com/248938 [ Linux Win ] virtual/threaded/animations/animation-border-overflow.html [ Pass Timeout ]
crbug.com/248938 virtual/threaded/animations/animation-direction-normal.html [ Pass ImageOnlyFailure Timeout ]
crbug.com/320477 [ Debug ] inspector/sources/debugger/debugger-pause-in-internal.html [ Pass Crash ]
-crbug.com/403819 compositing/ancestor-painted-layer-should-appear.html [ ImageOnlyFailure ]
-crbug.com/403819 virtual/softwarecompositing/ancestor-painted-layer-should-appear.html [ ImageOnlyFailure ]
-
# Printing Layout broken in these tests.
crbug.com/377696 printing/setPrinting.html [ Skip ]
crbug.com/377696 printing/width-overflow.html [ Skip ]
crbug.com/350853 fast/pagination/auto-height.html [ Skip ]
crbug.com/350853 fast/pagination/auto-height-with-break.html [ Skip ]
+crbug.com/408075 compositing/direct-image-compositing.html [ NeedsRebaseline ]
+crbug.com/408075 compositing/generated-content.html [ NeedsRebaseline ]
+crbug.com/408075 compositing/geometry/root-layer-update.html [ NeedsRebaseline ]
+crbug.com/408075 compositing/geometry/transfrom-origin-on-zero-size-layer.html [ NeedsRebaseline ]
+crbug.com/408075 compositing/gestures/gesture-tapHighlight-pixel-rotated-link.html [ NeedsRebaseline ]
+crbug.com/408075 compositing/repaint/become-overlay-composited-layer.html [ NeedsRebaseline ]
+crbug.com/408075 compositing/sibling-positioning.html [ NeedsRebaseline ]
+crbug.com/408075 css1/basic/containment.html [ NeedsRebaseline ]
+crbug.com/408075 css1/basic/inheritance.html [ NeedsRebaseline ]
+crbug.com/408075 css1/box_properties/acid_test.html [ NeedsRebaseline ]
+crbug.com/408075 css1/color_and_background/background_repeat.html [ NeedsRebaseline ]
+crbug.com/408075 css1/pseudo/anchor.html [ NeedsRebaseline ]
+crbug.com/408075 css1/text_properties/text_decoration.html [ NeedsRebaseline ]
+crbug.com/408075 css2.1/20110323/c543-txt-decor-000.html [ NeedsRebaseline ]
+crbug.com/408075 css2.1/t051103-c21-activ-ln-00-e-i.html [ NeedsRebaseline ]
+crbug.com/408075 css2.1/t051103-c21-focus-ln-00-e-i.html [ NeedsRebaseline ]
+crbug.com/408075 css2.1/t051103-c21-hover-ln-00-e-i.html [ NeedsRebaseline ]
+crbug.com/408075 css2.1/t0511-c21-pseud-link-00-e.html [ NeedsRebaseline ]
+crbug.com/408075 css2.1/t0511-c21-pseud-link-01-e.html [ NeedsRebaseline ]
+crbug.com/408075 css2.1/t0511-c21-pseud-link-02-e.html [ NeedsRebaseline ]
+crbug.com/408075 css2.1/t0511-c21-pseud-link-03-e.html [ NeedsRebaseline ]
+crbug.com/408075 css2.1/t0602-c13-inh-underlin-00-e.html [ NeedsRebaseline ]
+crbug.com/408075 css2.1/t060403-c21-pseu-cls-00-e-i.html [ NeedsRebaseline ]
+crbug.com/408075 css2.1/t060403-c21-pseu-id-00-e-i.html [ NeedsRebaseline ]
+crbug.com/408075 css2.1/t09-c5526c-display-00-e.html [ NeedsRebaseline ]
+crbug.com/408075 css3/flexbox/button.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/html/css3-modsel-16.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/html/css3-modsel-17.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/html/css3-modsel-18a.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/html/css3-modsel-18.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/html/css3-modsel-19.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/html/css3-modsel-20.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/html/css3-modsel-21.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/html/css3-modsel-61.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/html/css3-modsel-62.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/html/css3-modsel-64.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/html/css3-modsel-65.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/html/css3-modsel-66.html [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xhtml/css3-modsel-16.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xhtml/css3-modsel-17.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xhtml/css3-modsel-18a.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xhtml/css3-modsel-18.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xhtml/css3-modsel-19.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xhtml/css3-modsel-20.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xhtml/css3-modsel-21.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xhtml/css3-modsel-61.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xhtml/css3-modsel-62.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xhtml/css3-modsel-64.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xhtml/css3-modsel-65.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xhtml/css3-modsel-66.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xml/css3-modsel-16.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xml/css3-modsel-17.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xml/css3-modsel-18a.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xml/css3-modsel-18.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xml/css3-modsel-19.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xml/css3-modsel-20.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xml/css3-modsel-21.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xml/css3-modsel-61.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xml/css3-modsel-62.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xml/css3-modsel-64.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xml/css3-modsel-65.xml [ NeedsRebaseline ]
+crbug.com/408075 css3/selectors3/xml/css3-modsel-66.xml [ NeedsRebaseline ]
+crbug.com/408075 editing/deleting/delete-4083333-fix.html [ NeedsRebaseline ]
+crbug.com/408075 editing/deleting/delete-br-013.html [ NeedsRebaseline ]
+crbug.com/408075 editing/execCommand/5142012-1.html [ NeedsRebaseline ]
+crbug.com/408075 editing/execCommand/5142012-2.html [ NeedsRebaseline ]
+crbug.com/408075 editing/execCommand/5190926.html [ NeedsRebaseline ]
+crbug.com/408075 editing/inserting/4840662.html [ NeedsRebaseline ]
+crbug.com/408075 editing/inserting/4959067.html [ NeedsRebaseline ]
+crbug.com/408075 editing/inserting/5156401-2.html [ NeedsRebaseline ]
+crbug.com/408075 editing/inserting/insert-div-022.html [ NeedsRebaseline ]
+crbug.com/408075 editing/inserting/insert-div-023.html [ NeedsRebaseline ]
+crbug.com/408075 editing/inserting/insert-div-024.html [ NeedsRebaseline ]
+crbug.com/408075 editing/inserting/insert-div-026.html [ NeedsRebaseline ]
+crbug.com/408075 editing/inserting/insert-paragraph-05.html [ NeedsRebaseline ]
+crbug.com/408075 editing/inserting/return-key-with-selection-001.html [ NeedsRebaseline ]
+crbug.com/408075 editing/inserting/return-key-with-selection-002.html [ NeedsRebaseline ]
+crbug.com/408075 editing/inserting/return-key-with-selection-003.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/5071074-2.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/5071074.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/5075944.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/5134759.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/5156401-1.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/5601583-1.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/drop-text-without-selection.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-4038267-fix.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-line-endings-001.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-line-endings-002.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-line-endings-003.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-line-endings-004.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-line-endings-005.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-line-endings-006.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-line-endings-007.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-line-endings-008.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-line-endings-009.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-line-endings-010.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-match-style-002.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-text-013.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-text-014.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-text-016.html [ NeedsRebaseline ]
+crbug.com/408075 editing/pasteboard/paste-text-019.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/4402375.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/6476.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/7152-1.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/7152-2.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/after-line-wrap.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/click-start-of-line.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/contains-boundaries.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/editable-links.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/inline-closest-leaf-child.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/leave-requested-block.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/line-wrap-2.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/previous-line-position.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/range-between-block-and-inline.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/replace-selection-1.html [ NeedsRebaseline ]
+crbug.com/408075 editing/selection/select-text-overflow-ellipsis.html [ NeedsRebaseline ]
+crbug.com/408075 editing/style/block-styles-007.html [ NeedsRebaseline ]
+crbug.com/408075 editing/style/smoosh-styles-003.html [ NeedsRebaseline ]
+crbug.com/408075 editing/style/style-3998892-fix.html [ NeedsRebaseline ]
+crbug.com/408075 fast/backgrounds/background-clip-text.html [ NeedsRebaseline ]
+crbug.com/408075 fast/backgrounds/background-inherit-color-bug.html [ NeedsRebaseline ]
+crbug.com/408075 fast/backgrounds/body-generated-image-propagated-to-root.html [ NeedsRebaseline ]
+crbug.com/408075 fast/backgrounds/repeat/mask-negative-offset-repeat.html [ NeedsRebaseline ]
+crbug.com/408075 fast/backgrounds/repeat/negative-offset-repeat.html [ NeedsRebaseline ]
+crbug.com/408075 fast/backgrounds/repeat/negative-offset-repeat-transformed.html [ NeedsRebaseline ]
+crbug.com/408075 fast/backgrounds/size/zero.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/basic/011.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/basic/018.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/basic/adding-near-anonymous-block.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/basic/text-indent-rtl.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/basic/truncation-rtl.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/basic/white-space-pre-wraps.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/001.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/002.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/centered-float-avoidance-complexity.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/float-in-float-hit-testing.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/float-in-float-painting.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/independent-align-positioning.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/intruding-painted-twice.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/marquee-shrink-to-avoid-floats.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/nopaint-after-layer-destruction2.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/nopaint-after-layer-destruction.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/shrink-to-fit-width.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/table-relayout.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/float/vertical-move-relayout.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/001.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/005.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/010.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/011.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/012.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/015.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/016.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/017.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/019.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/020.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/056.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/059.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/block-inside-inline/001.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/block-inside-inline/005.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/block-inside-inline/010.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/block-inside-inline/011.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/block-inside-inline/012.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/block-inside-inline/015.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/block-inside-inline/016.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/block-inside-inline/017.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/block-inside-inline/019.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/margin-collapse/block-inside-inline/020.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/positioning/001.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/positioning/auto/007.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/positioning/auto/vertical-lr/007.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/positioning/auto/vertical-rl/007.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/positioning/height-change.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/positioning/relayout-on-position-change.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/positioning/vertical-lr/001.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/positioning/vertical-rl/001.html [ NeedsRebaseline ]
+crbug.com/408075 fast/block/positioning/window-height-change.html [ NeedsRebaseline ]
+crbug.com/408075 fast/borders/border-radius-huge-assert.html [ NeedsRebaseline ]
+crbug.com/408075 fast/box-shadow/spread-multiple-inset.html [ NeedsRebaseline ]
+crbug.com/408075 fast/canvas/setWidthResetAfterForcedRender.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css3-text/css3-text-decoration/repaint/repaint-text-decoration-style.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css3-text/css3-text-decoration/text-decoration-style.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css3-text/css3-text-decoration/text-underline-position/text-underline-position-all.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css3-text/css3-text-decoration/text-underline-position/text-underline-position-auto.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css3-text/css3-text-decoration/text-underline-position/text-underline-position-under.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css3-text/css3-text-decoration/text-underline-position/text-underline-position-under-out-of-flow.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/acid2.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/beforeSelectorOnCodeElement.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/border-radius-outline-offset.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/child-style-can-override-visited-style.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/clip-zooming.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/compare-content-style.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/css1_forward_compatible_parsing.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/css-imports.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/css-properties-position-relative-as-parent-fixed.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/empty-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/error-in-last-decl.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/ex-after-font-variant.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/find-next-layer.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/first-child-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/first-letter-capitalized.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/first-letter-detach.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/first-letter-float-after-float.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/first-letter-float.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/first-letter-hover.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/first-letter-recalculation.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/first-letter-visibility.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/first-line-text-decoration.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/first-line-text-decoration-inherited-from-parent.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/first-of-type-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/focus-ring-detached.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/focus-ring-multiline.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/focus-ring-outline-color.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/focus-ring-outline-offset.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/focus-ring-outline-width.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/font-face-opentype.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/font-face-synthetic-bold-italic.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/font-face-weight-matching.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/font-shorthand-weight-only.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css-generated-content/001.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css-generated-content/010.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css-generated-content/016.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css-generated-content/after-duplicated-after-split.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css-generated-content/beforeAfter-interdocument.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css-generated-content/before-with-first-letter.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css-generated-content/hover-style-change.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css-generated-content/wbr-with-before-content.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/hover-subselector.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/import-rule-regression-11590.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/invalidation-errors-2.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/invalidation-errors-3.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/invalidation-errors.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/invalid-percentage-property.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/last-child-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/last-of-type-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/layerZOrderCrash.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/line-height.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/line-thickness-underline-strikethrough-overline.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/nth-child-dynamic.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/only-child-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/only-of-type-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/resize-corner-tracking.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/resize-corner-tracking-transformed.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/resize-corner-tracking-transformed-iframe.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/rtl-ordering.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/text-overflow-ellipsis.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/text-overflow-ellipsis-strict.html [ NeedsRebaseline ]
+crbug.com/408075 fast/css/universal-hover-quirk.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dom/34176.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dom/anchor-text.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dom/children-nodes.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dom/clone-node-dynamic-style.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dom/Element/class-attribute-whitespace.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dom/HTMLHeadElement/head-link-style-href-check.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dom/HTMLLinkElement/pending-stylesheet-count.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dynamic/anonymous-block-orphaned-lines.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dynamic/containing-block-change.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dynamic/create-renderer-for-whitespace-only-text.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dynamic/float-in-trailing-whitespace-after-last-line-break.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dynamic/float-withdrawal.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dynamic/insert-before-table-part-in-continuation.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dynamic/layer-hit-test-crash.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dynamic/outerHTML-doc.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dynamic/outerHTML-img.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dynamic/selection-highlight-adjust.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dynamic/staticY-marking-parents-regression.html [ NeedsRebaseline ]
+crbug.com/408075 fast/dynamic/view-overflow.html [ NeedsRebaseline ]
+crbug.com/408075 fast/events/reveal-link-when-focused.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/button-cannot-be-nested.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/button-inner-block-reuse.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/button-text-transform.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/button-white-space.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/control-clip-overflow.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/datalist/input-appearance-range-with-padding-with-datalist.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/fieldset-legend-padding-unclipped-fieldset-border.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/floating-textfield-relayout.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/form-hides-table.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/input-readonly-autoscroll.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/listbox-scrollbar-incremental-load.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/menulist-clip.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/menulist-option-wrap.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/plaintext-mode-2.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/range/slider-thumb-shared-style.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/search-rtl.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/select-change-listbox-size.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/select-disabled-appearance.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/select-display-none-style-resolve.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/select-item-background-clip.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/select-writing-direction-natural.html [ NeedsRebaseline ]
+crbug.com/408075 fast/forms/visual-hebrew-text-field.html [ NeedsRebaseline ]
+crbug.com/408075 fast/frames/frameset-style-recalc.html [ NeedsRebaseline ]
+crbug.com/408075 fast/html/marquee-scroll.html [ NeedsRebaseline ]
+crbug.com/408075 fast/images/animated-gif-with-offsets.html [ NeedsRebaseline ]
+crbug.com/408075 fast/images/image-map-anchor-children.html [ NeedsRebaseline ]
+crbug.com/408075 fast/images/imagemap-focus-ring-outline-color-not-inherited-from-map.html [ NeedsRebaseline ]
+crbug.com/408075 fast/inline-block/14498-positionForCoordinates.html [ NeedsRebaseline ]
+crbug.com/408075 [ Mac Win Linux ] fast/inline-block/overflow-clip.html [ NeedsRebaseline ]
+crbug.com/408075 fast/inline/br-text-decoration.html [ NeedsRebaseline ]
+crbug.com/408075 fast/inline/drawStyledEmptyInlines.html [ NeedsRebaseline ]
+crbug.com/408075 fast/inline/drawStyledEmptyInlinesWithWS.html [ NeedsRebaseline ]
+crbug.com/408075 fast/inline/inline-box-background.html [ NeedsRebaseline ]
+crbug.com/408075 fast/inline/inline-box-background-long-image.html [ NeedsRebaseline ]
+crbug.com/408075 fast/inline/inline-box-background-repeat-x.html [ NeedsRebaseline ]
+crbug.com/408075 fast/inline/inline-box-background-repeat-y.html [ NeedsRebaseline ]
+crbug.com/408075 fast/inline/inline-focus-ring.html [ NeedsRebaseline ]
+crbug.com/408075 fast/invalid/021.html [ NeedsRebaseline ]
+crbug.com/408075 fast/invalid/missing-address-end-tag.html [ NeedsRebaseline ]
+crbug.com/408075 fast/invalid/missing-dl-end-tag.html [ NeedsRebaseline ]
+crbug.com/408075 fast/invalid/missing-dt-end-tag.html [ NeedsRebaseline ]
+crbug.com/408075 fast/invalid/missing-font-end-tag.html [ NeedsRebaseline ]
+crbug.com/408075 fast/layers/inline-dirty-z-order-lists.html [ NeedsRebaseline ]
+crbug.com/408075 fast/layers/normal-flow-hit-test.html [ NeedsRebaseline ]
+crbug.com/408075 fast/layers/opacity-outline.html [ NeedsRebaseline ]
+crbug.com/408075 fast/layers/opacity-transforms.html [ NeedsRebaseline ]
+crbug.com/408075 fast/layers/scroll-rect-to-visible.html [ NeedsRebaseline ]
+crbug.com/408075 fast/layers/video-layer.html [ NeedsRebaseline ]
+crbug.com/408075 fast/lists/002.html [ NeedsRebaseline ]
+crbug.com/408075 fast/lists/002-vertical.html [ NeedsRebaseline ]
+crbug.com/408075 fast/lists/003.html [ NeedsRebaseline ]
+crbug.com/408075 fast/lists/003-vertical.html [ NeedsRebaseline ]
+crbug.com/408075 fast/lists/006.html [ NeedsRebaseline ]
+crbug.com/408075 fast/lists/006-vertical.html [ NeedsRebaseline ]
+crbug.com/408075 fast/lists/list-style-none-crash.html [ NeedsRebaseline ]
+crbug.com/408075 fast/lists/marker-before-empty-inline.html [ NeedsRebaseline ]
+crbug.com/408075 fast/lists/marker-image-error.html [ NeedsRebaseline ]
+crbug.com/408075 fast/lists/markers-in-selection.html [ NeedsRebaseline ]
+crbug.com/408075 fast/lists/scrolled-marker-paint.html [ NeedsRebaseline ]
+crbug.com/408075 fast/multicol/float-multicol.html [ NeedsRebaseline ]
+crbug.com/408075 fast/multicol/vertical-lr/float-multicol.html [ NeedsRebaseline ]
+crbug.com/408075 fast/multicol/vertical-rl/float-multicol.html [ NeedsRebaseline ]
+crbug.com/408075 fast/overflow/float-in-relpositioned.html [ NeedsRebaseline ]
+crbug.com/408075 fast/overflow/hit-test-overflow-controls.html [ NeedsRebaseline ]
+crbug.com/408075 fast/overflow/image-selection-highlight.html [ NeedsRebaseline ]
+crbug.com/408075 fast/overflow/line-clamp.html [ NeedsRebaseline ]
+crbug.com/408075 fast/overflow/overflow-rtl-inline-scrollbar.html [ NeedsRebaseline ]
+crbug.com/408075 fast/overflow/position-fixed-transform-clipping.html [ NeedsRebaseline ]
+crbug.com/408075 fast/overflow/scrollbar-position-update.html [ NeedsRebaseline ]
+crbug.com/408075 fast/parser/broken-comments-vs-parsing-mode.html [ NeedsRebaseline ]
+crbug.com/408075 fast/parser/nofoo-tags-inside-paragraph.html [ NeedsRebaseline ]
+crbug.com/408075 fast/reflections/inline-crash.html [ NeedsRebaseline ]
+crbug.com/408075 fast/reflections/reflection-overflow-hidden.html [ NeedsRebaseline ]
+crbug.com/408075 fast/repaint/flexible-box-overflow-horizontal.html [ NeedsRebaseline ]
+crbug.com/408075 fast/repaint/flexible-box-overflow.html [ NeedsRebaseline ]
+crbug.com/408075 fast/repaint/focus-layers.html [ NeedsRebaseline ]
+crbug.com/408075 fast/repaint/layer-outline-horizontal.html [ NeedsRebaseline ]
+crbug.com/408075 fast/repaint/layer-outline.html [ NeedsRebaseline ]
+crbug.com/408075 fast/repaint/list-marker.html [ NeedsRebaseline ]
+crbug.com/408075 fast/repaint/shadow-multiple.html [ NeedsRebaseline ]
+crbug.com/408075 fast/repaint/text-shadow-horizontal.html [ NeedsRebaseline ]
+crbug.com/408075 fast/repaint/text-shadow.html [ NeedsRebaseline ]
+crbug.com/408075 fast/replaced/absolute-position-percentage-height.html [ NeedsRebaseline ]
+crbug.com/408075 fast/replaced/applet-disabled-positioned.html [ NeedsRebaseline ]
+crbug.com/408075 fast/replaced/applet-rendering-java-disabled.html [ NeedsRebaseline ]
+crbug.com/408075 fast/replaced/image-resize-width.html [ NeedsRebaseline ]
+crbug.com/408075 fast/replaced/image-solid-color-with-alpha.html [ NeedsRebaseline ]
+crbug.com/408075 fast/replaced/inline-box-wrapper-handover.html [ NeedsRebaseline ]
+crbug.com/408075 fast/replaced/max-width-percent.html [ NeedsRebaseline ]
+crbug.com/408075 fast/replaced/percent-height-in-anonymous-block-in-table.html [ NeedsRebaseline ]
+crbug.com/408075 fast/replaced/selection-rect-in-table-cell.html [ NeedsRebaseline ]
+crbug.com/408075 fast/replaced/selection-rect-transform.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/016.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/017.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/018.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/019.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/020.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/021.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/061.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/062.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/064.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/065.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/066.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/166.html [ NeedsRebaseline ]
+crbug.com/408075 fast/selectors/visited-descendant.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/100-percent-cell-width.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/absolute-table-at-bottom.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/add-before-anonymous-child.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/border-collapsing/003.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/border-collapsing/003-vertical.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/border-collapsing/004.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/border-collapsing/004-vertical.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/border-collapsing/border-collapsing-head-foot.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/border-collapsing/border-collapsing-head-foot-vertical.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/border-collapsing/rtl-border-collapsing.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/border-collapsing/rtl-border-collapsing-vertical.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/cell-absolute-child.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/click-near-anonymous-table.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/edge-offsets.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/fixed-table-non-cell-in-row.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/form-with-table-style.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/generated-caption.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/insert-before-anonymous-ancestors.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/insert-cell-before-form.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/insert-row-before-form.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/prepend-in-anonymous-table.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/row-height-recalc.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/rtl-cell-display-none-assert.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/stale-grid-crash.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/text-field-baseline.html [ NeedsRebaseline ]
+crbug.com/408075 fast/table/unused-percent-heights.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/atsui-partial-selection.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/atsui-small-caps-punctuation-size.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/atsui-spacing-features.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text-autosizing/hackernews-comments.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/basic/008.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/basic/015.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/break-word.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/capitalize-boundaries.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/capitalize-empty-generated-string.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/capitalize-preserve-nbsp.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/complex-text-opacity.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/decorations-transformed.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/decorations-with-text-combine.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/delete-hard-break-character.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/fallback-for-custom-font.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/fallback-traits-fixup.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/font-initial.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/fractional-word-and-letter-spacing-with-kerning.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/in-rendered-text-rtl.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/international/001.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/international/003.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/international/bidi-AN-after-empty-run.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/international/bidi-LDB-2-CSS.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/international/bidi-LDB-2-formatting-characters.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/international/bidi-LDB-2-HTML.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/international/bidi-neutral-directionality-paragraph-start.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/international/danda-space.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/international/khmer-selection.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/international/rtl-caret.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/international/rtl-white-space-pre-wrap.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/international/thai-baht-space.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/justified-selection.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/line-breaks.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/midword-break-after-breakable-char.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/midword-break-hang.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/monospace-width-cache.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/reset-emptyRun.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/selection-hard-linebreak.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/stroking-decorations.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/trailing-white-space-2.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/trailing-white-space.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/wbr.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/wbr-in-pre-crash.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/whitespace/020.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/whitespace/024.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/whitespace/pre-wrap-last-char.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/whitespace/pre-wrap-overflow-selection.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/whitespace/pre-wrap-spaces-after-newline.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/whitespace/span-in-word-space-causes-overflow.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/wide-zero-width-space.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/word-break-run-rounding.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/word-break-soft-hyphen.html [ NeedsRebaseline ]
+crbug.com/408075 fast/text/word-space.html [ NeedsRebaseline ]
+crbug.com/408075 fast/transforms/transformed-caret.html [ NeedsRebaseline ]
+crbug.com/408075 fast/transforms/transform-positioned-ancestor.html [ NeedsRebaseline ]
+crbug.com/408075 fast/transforms/transforms-with-zoom.html [ NeedsRebaseline ]
+crbug.com/408075 fast/writing-mode/english-lr-text.html [ NeedsRebaseline ]
+crbug.com/408075 http/tests/misc/acid2.html [ NeedsRebaseline ]
+crbug.com/408075 http/tests/misc/iframe404.html [ NeedsRebaseline ]
+crbug.com/408075 http/tests/multipart/invalid-image-data.html [ NeedsRebaseline ]
+crbug.com/408075 http/tests/uri/css-href.php [ NeedsRebaseline ]
+crbug.com/408075 ietestcenter/css3/text/textshadow-002.htm [ NeedsRebaseline ]
+crbug.com/408075 svg/custom/dominant-baseline-hanging.svg [ NeedsRebaseline ]
+crbug.com/408075 svg/custom/foreign-object-skew.svg [ NeedsManualRebaseline ]
+crbug.com/408075 svg/custom/rootmost-svg-xy-attrs.xhtml [ NeedsRebaseline ]
+crbug.com/408075 svg/custom/svg-fonts-without-missing-glyph.xhtml [ NeedsManualRebaseline ]
+crbug.com/408075 svg/wicd/test-rightsizing-a.xhtml [ NeedsRebaseline ]
+crbug.com/408075 svg/wicd/test-scalable-background-image1.xhtml [ NeedsRebaseline ]
+crbug.com/408075 svg/wicd/test-scalable-background-image2.xhtml [ NeedsRebaseline ]
+crbug.com/408075 svg/zoom/page/zoom-foreignObject.svg [ NeedsRebaseline ]
+crbug.com/408075 svg/zoom/text/zoom-foreignObject.svg [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug10633.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug109043.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug126742.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug128229.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug17130-1.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug17130-2.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug17138.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug18664.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug20579.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug20804.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug22019.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug23235.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug2479-1.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug2479-3.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug2479-4.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug275625.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug2962.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug3309-1.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug4849-2.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug51140.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug5538.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug59354.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug73321.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug7342.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug88035-1.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug88035-2.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug8950.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/bugs/bug92868.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/collapsing_borders/bug41262-3.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/core/bloomberg.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/bugs/bug1010.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/bugs/bug1055-2.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/bugs/bug2479-5.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_border-table-cell.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_border-table-column-group.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_border-table-column.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_border-table.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_border-table-quirks.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_border-table-row-group.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_border-table-row.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_fixed-bg.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_layers-hide.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_position-table-cell.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_position-table-column-group.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_position-table-column.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_position-table-row-group.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla_expected_failures/marvin/backgr_position-table-row.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/marvin/backgr_index.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/marvin/backgr_layers-opacity.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/marvin/backgr_position-table.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/marvin/backgr_simple-table-cell.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/marvin/backgr_simple-table-column-group.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/marvin/backgr_simple-table-column.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/marvin/backgr_simple-table.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/marvin/backgr_simple-table-row-group.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/marvin/backgr_simple-table-row.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/other/wa_table_thtd_rowspan.html [ NeedsRebaseline ]
+crbug.com/408075 tables/mozilla/other/wa_table_tr_align.html [ NeedsRebaseline ]
+crbug.com/408075 transforms/2d/hindi-rotated.html [ NeedsRebaseline ]
+crbug.com/408075 virtual/antialiasedtext/fast/text/decorations-transformed.html [ NeedsRebaseline ]
+crbug.com/408075 virtual/antialiasedtext/fast/text/decorations-with-text-combine.html [ NeedsRebaseline ]
+crbug.com/408075 virtual/antialiasedtext/fast/text/international/rtl-white-space-pre-wrap.html [ NeedsRebaseline ]
+crbug.com/408075 virtual/antialiasedtext/fast/text/line-breaks.html [ NeedsRebaseline ]
+crbug.com/408075 virtual/antialiasedtext/fast/text/stroking-decorations.html [ NeedsRebaseline ]
+crbug.com/408075 virtual/antialiasedtext/fast/text/trailing-white-space-2.html [ NeedsRebaseline ]
+crbug.com/408075 virtual/antialiasedtext/fast/text/trailing-white-space.html [ NeedsRebaseline ]
+crbug.com/408075 virtual/deferred/fast/images/animated-gif-with-offsets.html [ NeedsRebaseline ]
+crbug.com/408075 virtual/deferred/fast/images/image-map-anchor-children.html [ NeedsRebaseline ]
+crbug.com/408075 virtual/deferred/fast/images/imagemap-focus-ring-outline-color-not-inherited-from-map.html [ NeedsRebaseline ]
+crbug.com/408075 virtual/gpu/fast/canvas/setWidthResetAfterForcedRender.html [ NeedsRebaseline ]
+
crbug.com/374569 [ Win ] http/tests/navigation/beacon-cross-origin.html [ Pass Failure Timeout ]
crbug.com/374572 svg/filters/feImage-target-reappend-to-document.svg [ Pass Failure ]
crbug.com/374572 svg/dynamic-updates/SVGImageElement-svgdom-preserveAspectRatio-prop.html [ Pass ImageOnlyFailure ]
crbug.com/313438 svg/custom/relative-sized-use-on-symbol.xhtml [ NeedsManualRebaseline ]
crbug.com/313438 svg/custom/relative-sized-use-without-attributes-on-symbol.xhtml [ NeedsManualRebaseline ]
-crbug.com/390488 [ SnowLeopard Debug ] svg/custom/foreign-object-skew.svg [ ImageOnlyFailure ]
+#crbug.com/390488 [ SnowLeopard Debug ] svg/custom/foreign-object-skew.svg [ ImageOnlyFailure ]
crbug.com/375565 [ Mac ] fast/repaint/caret-with-transformation.html [ Pass Failure ]
crbug.com/394762 virtual/deferred/inspector/timeline/timeline-flame-chart-automatically-size-window.html [ Skip ]
crbug.com/394762 virtual/implsidepainting/inspector/timeline/timeline-flame-chart-automatically-size-window.html [ Skip ]
+crbug.com/408031 svg/batik/filters/feTile.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/batik/filters/filterRegions.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/batik/text/smallFonts.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/batik/text/textFeatures.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/custom/text-filter.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/dynamic-updates/SVGFEImageElement-dom-preserveAspectRatio-attr.html [ NeedsRebaseline ]
+crbug.com/408031 svg/dynamic-updates/SVGFEImageElement-svgdom-preserveAspectRatio-prop.html [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/feComposite.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/feGaussianBlur.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/feOffset.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/feTile.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/filter-on-tspan.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/filter-placement-issue.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/filter-rounding-issues.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/filterRes.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/filterRes2.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/feImage-subregions.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/feImage-subregions-preseveAspectRatio-none.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/feImage-subregions-preseveAspectRatio-none-with-viewBox.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/subRegion-two-effects.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/parent-children-with-same-filter.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/filters/subRegion-one-effect.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/repaint/filter-repaint.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/repaint/paintorder-filtered.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/W3C-SVG-1.1/filters-color-01-b.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/W3C-SVG-1.1/filters-gauss-01-b.svg [ NeedsRebaseline ]
+crbug.com/408031 svg/W3C-SVG-1.1/filters-morph-01-f.svg [ NeedsRebaseline ]
+
# Assertion failures seen only on Slow Leopard.
crbug.com/374961 [ SnowLeopard Debug ] inspector/sources/debugger/frameworks-skip-step-in.html [ Crash ]
crbug.com/374961 [ SnowLeopard Debug ] inspector/sources/debugger/frameworks-steppings.html [ Crash ]
crbug.com/374961 [ SnowLeopard Debug ] inspector/sources/debugger/frameworks-with-async-callstack.html [ Crash ]
crbug.com/374961 [ SnowLeopard Debug ] inspector/sources/debugger/async-callstack-get-as-string.html [ Crash ]
+crbug.com/395425 [ Win7 ] css1/basic/class_as_selector.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/basic/comments.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/basic/containment.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/basic/contextual_selectors.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/basic/grouping.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/basic/id_as_selector.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/basic/inheritance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/acid_test.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_bottom.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_bottom_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_bottom_width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_bottom_width_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_color.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_color_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_left.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_left_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_left_width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_left_width_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_right.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_right_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_right_width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_right_width_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_style.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_style_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_top.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_top_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_top_width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_top_width_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/border_width_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/clear.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/clear_float.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/float.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/float_elements_in_series.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/float_margin.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/margin.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/margin_bottom.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/margin_bottom_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/margin_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/margin_left.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/margin_left_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/margin_right.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/margin_right_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/margin_top.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/margin_top_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/padding.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/padding_bottom.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/padding_bottom_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/padding_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/padding_left.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/padding_left_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/padding_right.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/padding_right_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/padding_top.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/padding_top_inline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/box_properties/width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/cascade/cascade_order.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/cascade/important.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/classification/display.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/classification/list_style.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/classification/list_style_image.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/classification/list_style_position.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/classification/list_style_type.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/classification/white_space.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/color_and_background/background.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/color_and_background/background_attachment.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/color_and_background/background_color.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/color_and_background/background_image.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/color_and_background/background_position.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/color_and_background/background_repeat.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/color_and_background/color.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/conformance/forward_compatible_parsing.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/font_properties/font.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/font_properties/font_family.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/font_properties/font_size.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/font_properties/font_style.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/font_properties/font_variant.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/font_properties/font_weight.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/formatting_model/canvas.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/formatting_model/floating_elements.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/formatting_model/height_of_lines.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/formatting_model/inline_elements.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/formatting_model/replaced_elements.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/formatting_model/vertical_formatting.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/pseudo/anchor.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/pseudo/firstletter.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/pseudo/firstline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/pseudo/multiple_pseudo_elements.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/pseudo/pseudo_elements_in_selectors.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/text_properties/letter_spacing.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/text_properties/line_height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/text_properties/text_align.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/text_properties/text_decoration.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/text_properties/text_indent.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/text_properties/text_transform.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/text_properties/vertical_align.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/text_properties/word_spacing.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/units/color_units.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/units/length_units.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/units/percentage_units.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/units/urls.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/20110323/replaced-elements-001.htm [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/20110323/table-height-algorithm-023.htm [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/20110323/table-height-algorithm-024.htm [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t050803-c14-classes-00-e.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t0509-c15-ids-01-e.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t051201-c23-first-line-00-b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t051202-c26-psudo-nest-00-c.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t0602-c13-inh-underlin-00-e.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t09-c5526c-display-00-e.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t0905-c5525-fltwidth-00-c-g.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t1503-c522-font-family-00-b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t1505-c524-font-var-00-b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t1507-c526-font-sz-00-b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t1508-c527-font-00-b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t1508-c527-font-04-b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t1508-c527-font-05-b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t1508-c527-font-06-b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t1508-c527-font-07-b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t1508-c527-font-09-b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css2.1/t1508-c527-font-10-c.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/filters/effect-reference-zoom-hw.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/filters/effect-reference-zoom.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/flexbox/button.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/html/css3-modsel-161.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/html/css3-modsel-18b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/html/css3-modsel-19b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/html/css3-modsel-23.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/html/css3-modsel-24.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/html/css3-modsel-64.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/html/css3-modsel-68.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/html/css3-modsel-69.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xhtml/css3-modsel-161.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xhtml/css3-modsel-18b.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xhtml/css3-modsel-19b.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xhtml/css3-modsel-23.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xhtml/css3-modsel-24.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xhtml/css3-modsel-64.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xhtml/css3-modsel-68.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xhtml/css3-modsel-69.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xml/css3-modsel-161.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xml/css3-modsel-18b.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xml/css3-modsel-19b.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xml/css3-modsel-23.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xml/css3-modsel-24.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xml/css3-modsel-64.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xml/css3-modsel-68.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/selectors3/xml/css3-modsel-69.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/caret/caret-position.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/deleting/merge-whitespace-pre.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/execCommand/format-block-with-trailing-br.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/input/caret-at-the-edge-of-input.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/input/reveal-caret-of-multiline-input.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/inserting/4278698.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/inserting/4960120-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/inserting/before-after-input-element.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/inserting/paragraph-separator-03.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/inserting/typing-at-end-of-line.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/pasteboard/4641033.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/pasteboard/4806874.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/pasteboard/4944770-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/pasteboard/4944770-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/pasteboard/drop-text-without-selection.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/pasteboard/innerText-inline-table.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/pasteboard/input-field-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/pasteboard/pasting-tabs.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/3690703-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/3690703.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/3690719.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/4397952.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/4975120.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/5240265.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/caret-before-select.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/designmode-no-caret.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/doubleclick-crash.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/drag-select-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/replaced-boundaries-3.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/select-box.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/select-element-paragraph-boundary.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/selection-3748164-fix.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/selection-button-text.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/triple-click-in-pre.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/style/font-family-with-space.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/backgrounds/quirks-mode-line-box-backgrounds.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/block/basic/011.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/block/basic/014.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/block/basic/015.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/block/basic/minheight.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/block/float/float-avoidance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/block/float/shrink-to-avoid-float-complexity.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/block/margin-collapse/103.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/block/positioning/047.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/block/positioning/inline-block-relposition.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/borders/border-fit.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/box-sizing/percentage-height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css-generated-content/011.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css-generated-content/014.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/beforeSelectorOnCodeElement.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/continuationCrash.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/css-properties-position-relative-as-parent-fixed.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/empty-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/first-child-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/first-of-type-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/font-size-negative.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/h1-in-section-elements.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/hover-subselector.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/imageTileOpacity.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/invalidation-errors-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/invalidation-errors.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/last-child-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/last-of-type-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/line-height-determined-by-primary-font.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/line-height-font-order.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/line-height-negative.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/line-height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/margin-top-bottom-dynamic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/only-child-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/only-of-type-pseudo-class.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/outline-auto-empty-rects.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/rem-dynamic-scaling.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/resize-corner-tracking.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/rtl-ordering.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/text-overflow-input.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/universal-hover-quirk.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/word-space-extra.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/dom/HTMLInputElement/input-image-alt-text.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/dom/HTMLTableColElement/resize-table-using-col-width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/dom/HTMLTextAreaElement/reset-textarea.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/dom/row-inner-text.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/dynamic/008.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/encoding/utf-16-big-endian.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/encoding/utf-16-little-endian.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/events/autoscroll.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/events/context-no-deselect.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/files/file-in-input-display.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/flexbox/023.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/flexbox/024.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/001.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/003.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/004.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/HTMLOptionElement_label01.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/HTMLOptionElement_label02.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/HTMLOptionElement_label03.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/HTMLOptionElement_label04.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/HTMLOptionElement_label05.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/HTMLOptionElement_label06.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/HTMLOptionElement_label07.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/basic-buttons.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/basic-inputs.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/basic-selects.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/basic-textareas-quirks.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/basic-textareas.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/blankbuttons.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/button-align.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/button-cannot-be-nested.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/button-default-title.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/button-positioned.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/button-sizes.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/button-style-color.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/button-table-styles.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/button-text-transform.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/button-white-space.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/button/button-reset-focus-by-mouse-then-keydown.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/control-clip-overflow.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/control-clip.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/control-restrict-line-height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/disabled-select-change-index.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/encoding-test.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/fieldset-align.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/file/file-input-direction.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/file/file-input-disabled.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/file/input-file-re-render.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/floating-textfield-relayout.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/form-element-geometry.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/formmove3.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-align.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-appearance-bkcolor.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-appearance-default-bkcolor.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-appearance-disabled.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-appearance-focus.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-appearance-height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-appearance-preventDefault.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-appearance-readonly.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-appearance-selection.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-appearance-visibility.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-appearance-width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-baseline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-button-sizes.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-disabled-color.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-double-click-selection-gap-bug.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-field-text-truncated.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-first-letter.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-placeholder-visibility-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-placeholder-visibility-3.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-readonly-autoscroll.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-readonly-dimmed.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-readonly-empty.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-spaces.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-tab-shows-caret.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-table.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-text-click-inside.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-text-click-outside.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-text-double-click.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-text-drag-down.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-text-option-delete.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-text-scroll-left-on-blur.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-text-self-emptying-click.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-text-word-wrap.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-type-text-min-width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-value.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/input-width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/listbox-bidi-align.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/listbox-hit-test-zoomed.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/listbox-scrollbar-incremental-load.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/listbox-width-change.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/menulist-deselect-update.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/menulist-narrow-width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/menulist-no-overflow.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/menulist-option-wrap.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/menulist-restrict-line-height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/menulist-separator-painting.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/menulist-style-color.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/menulist-width-change.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/minWidthPercent.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/negativeLineHeight.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/number/number-appearance-datalist.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/number/number-appearance-rtl.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/number/number-appearance-spinbutton-layer.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/option-script.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/option-strip-whitespace.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/option-text-clip.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/placeholder-appearance-textarea.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/placeholder-position.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/placeholder-pseudo-style.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/plaintext-mode-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/search-cancel-button-style-sharing.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/search-display-none-cancel-button.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/search-rtl.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/search-vertical-alignment.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/search/search-appearance-basic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/searchfield-heights.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-align.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-background-none.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-baseline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-block-background.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-change-listbox-size.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-change-listbox-to-popup.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-change-popup-to-listbox.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-dirty-parent-pref-widths.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-disabled-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-empty-option-height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-initial-position.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-item-background-clip.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-list-box-with-height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-listbox-multiple-no-focusring.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-multiple-rtl.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-overflow-scroll-inherited.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-overflow-scroll.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-selected.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-size.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-style.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-visual-hebrew.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select-writing-direction-natural.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select/listbox-appearance-basic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select/listbox-with-display-none-option.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select/menulist-appearance-basic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/select/optgroup-rendering.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/selectlist-minsize.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/stuff-on-my-optgroup.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/submit/submit-appearance-basic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/submit/submit-focus-by-mouse-then-keydown.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/tabbing-input-iframe.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/targeted-frame-submission.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/text-style-color.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/text/text-appearance-basic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/text/text-appearance-datalist.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textAreaLineHeight.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textarea-align.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textarea-placeholder-pseudo-style.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textarea-placeholder-visibility-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textarea-placeholder-visibility-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textarea-scroll-height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textarea-scrollbar.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textarea-scrolled-type.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textarea-setinnerhtml.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textarea-width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textarea/textarea-appearance-basic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textfield-focus-ring.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/visual-hebrew-text-field.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/frames/iframe-scrolling-attribute.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/hidpi/resize-corner-hidpi.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/hidpi/video-controls-in-hidpi.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/html/details-no-summary4.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/html/details-open-javascript.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/html/details-open2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/html/details-open4.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/html/details-replace-summary-child.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/html/details-replace-text.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/html/keygen.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/html/listing.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/inline/nested-top-alignment.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/inline/vertical-align-text-bottom.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/invalid/014.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/invalid/019.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/invalid/junk-data.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/invalid/missing-end-tag.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/layers/video-layer.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/lists/003-vertical.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/lists/003.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/lists/dynamic-marker-crash.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/loader/text-document-wrapping.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/multicol/overflow-across-columns-percent-height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/multicol/overflow-across-columns.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/multicol/overflow-unsplittable.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/multicol/positive-leading.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/overflow/003.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/overflow/005.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/overflow/007.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/overflow/clip-rects-fixed-ancestor.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/overflow/infiniteRecursion.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/overflow/overflow-auto-table.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/overflow/overflow-x-y.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/overflow/scroll-nested-positioned-layer-in-overflow.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/overflow/scrollRevealButton.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/parser/001.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/parser/document-write-option.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/parser/entity-comment-in-textarea.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/parser/open-comment-in-textarea.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/parser/xhtml-alternate-entities.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/repaint/renderer-destruction-by-invalidateSelection-crash.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/replaced/replaced-breaking-mixture.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/replaced/replaced-breaking.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/replaced/three-selects-break.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/replaced/width100percent-button.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/replaced/width100percent-menulist.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/replaced/width100percent-searchfield.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/replaced/width100percent-textarea.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/replaced/width100percent-textfield.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/nested-ruby.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/ruby-beforeafter.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/ruby-empty-rt.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/ruby-inline-table.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/ruby-length.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/ruby-run-break.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/ruby-runs-spans.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/ruby-runs.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/ruby-simple-rp.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/ruby-simple.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/ruby-trailing.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/rubyDOM-insert-rt.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/rubyDOM-insert-text1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/rubyDOM-insert-text2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/rubyDOM-insert-text3.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/rubyDOM-remove-rt1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/rubyDOM-remove-rt2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/rubyDOM-remove-text1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/ruby/rubyDOM-remove-text2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/selectors/018b.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/selectors/064.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/spatial-navigation/snav-multiple-select-focusring.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/003.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/018.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/022.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/append-cells2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/border-collapsing/004-vertical.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/border-collapsing/004.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/colspanMinWidth-vertical.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/colspanMinWidth.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/frame-and-rules.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/invisible-cell-background.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/prepend-in-anonymous-table.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/remove-td-display-none.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/rowindex.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/spanOverlapRepaint.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/text-field-baseline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/hackernews-comments.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/css-table-lots-of-text-many-cells.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/css-table-single-cell-lots-of-text.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/fixed-table-lots-of-text-many-cells.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/fixed-table-single-cell-lots-of-text.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/lots-of-text-many-cells.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/narrow-percentage-width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/narrow-specified-width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/nested-table-wrapping.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/nested-tables.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/single-cell-lots-of-text.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/single-percent-width-cell-lots-of-text.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/table-cell-inflation.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/table-for-layout.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/wide-percentage-width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text-autosizing/tables/wide-specified-width.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/atsui-multiple-renderers.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/basic/001.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/basic/011.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/basic/013.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/basic/generic-family-changes.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/basic/generic-family-reset.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/break-word.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/capitalize-boundaries.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/drawBidiText.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/emphasis-complex.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/emphasis-vertical.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/emphasis.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/font-stretch.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/font-weight.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/international/bidi-listbox-atsui.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/international/bidi-listbox.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/international/bidi-menulist.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/international/hindi-spacing.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/international/khmer-selection.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/international/pop-up-button-text-alignment-and-direction.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/international/unicode-bidi-plaintext-in-textarea.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/international/unicode-bidi-plaintext.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/international/wrap-CJK-001.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/justify-ideograph-complex.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/justify-ideograph-simple.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/justify-ideograph-vertical.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/large-text-composed-char.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/selection-hard-linebreak.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/selection-rect-rounding.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/sub-pixel/text-scaling-pixel.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/textIteratorNilRenderer.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/updateNewFont.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/wbr-pre.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/wbr-styled.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/whitespace/001.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/whitespace/019.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/whitespace/023.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/whitespace/024.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/whitespace/029.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/whitespace/030.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/whitespace/pre-wrap-overflow-selection.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/whitespace/pre-wrap-spaces-after-newline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/word-break.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/transforms/bounding-rect-zoom.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/transforms/transformed-focused-text-input.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/writing-mode/Kusa-Makura-background-canvas.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/writing-mode/text-orientation-basic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/writing-mode/vertical-baseline-alignment.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/xsl/xslt-extra-content-at-end.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fonts/monospace.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] http/tests/filesystem/input-display.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] http/tests/misc/object-embedding-svg-delayed-size-negotiation-2.htm [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/audio-controls-rendering.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/audio-repaint.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/controls-after-reload.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/controls-strict.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/controls-styling-strict.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/controls-styling.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/controls-without-preload.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/media-controls-clone.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/media-document-audio-repaint.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/track/track-cue-rendering-horizontal.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/track/track-cue-rendering-vertical.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/video-controls-rendering.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/video-display-toggle.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/video-empty-source.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/video-no-audio.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] plugins/mouse-click-plugin-clears-selection.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] scrollbars/custom-scrollbar-with-incomplete-style.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] scrollbars/listbox-scrollbar-combinations.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] scrollbars/overflow-scrollbar-combinations.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/g-dirLTR-ubNone.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/g-dirLTR-ubOverride.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/g-dirRTL-ubNone.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/g-dirRTL-ubOverride.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-dirLTR-anchorEnd.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-dirLTR-anchorMiddle.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-dirLTR-anchorStart.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-dirNone-anchorEnd.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-dirNone-anchorMiddle.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-dirNone-anchorStart.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-dirRTL-anchorEnd.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-dirRTL-anchorMiddle.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-dirRTL-anchorStart.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-inherited-dirLTR-anchorEnd.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-inherited-dirLTR-anchorMiddle.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-inherited-dirLTR-anchorStart.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-inherited-dirRTL-anchorEnd.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-inherited-dirRTL-anchorMiddle.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-inherited-dirRTL-anchorStart.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-anchor-no-markup.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-dirLTR-ubNone.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-dirLTR-ubOverride.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-dirRTL-ubNone.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/text-dirRTL-ubOverride.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirLTR-ubEmbed-in-rtl-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirLTR-ubNone-in-rtl-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirLTR-ubOverride-in-default-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirLTR-ubOverride-in-ltr-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirLTR-ubOverride-in-rtl-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirNone-ubOverride-in-default-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirNone-ubOverride-in-ltr-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirNone-ubOverride-in-rtl-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirRTL-ubEmbed-in-default-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirRTL-ubEmbed-in-ltr-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirRTL-ubNone-in-default-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirRTL-ubNone-in-ltr-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirRTL-ubOverride-in-default-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirRTL-ubOverride-in-ltr-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-dirRTL-ubOverride-in-rtl-context.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-direction-ltr.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-I18N/tspan-direction-rtl.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/coords-dom-03-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/coords-units-03-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/filters-image-05-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/interact-pointer-03-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/linking-uri-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/painting-marker-05-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/painting-marker-06-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/styling-pres-02-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/text-tspan-02-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/types-dom-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/types-dom-02-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/types-dom-04-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/types-dom-05-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1-SE/types-dom-07-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-08-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-22-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-23-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-25-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-27-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-32-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-41-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-46-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-60-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-61-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-62-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-63-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-64-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-65-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-66-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-67-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-68-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-69-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-70-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-78-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/animate-elem-84-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/color-prof-01-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/coords-units-03-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/coords-viewattr-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/coords-viewattr-02-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/coords-viewattr-03-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/extend-namespace-01-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/filters-diffuse-01-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/filters-displace-01-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/filters-image-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/filters-light-01-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/filters-light-04-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/filters-specular-01-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/filters-tile-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/filters-turb-01-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/fonts-elem-05-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/fonts-elem-06-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/fonts-kern-01-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/linking-a-04-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/linking-a-05-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/linking-a-07-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/linking-uri-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/linking-uri-02-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/linking-uri-03-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/masking-mask-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/masking-path-05-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/metadata-example-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/painting-marker-01-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/painting-marker-02-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/paths-data-01-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/paths-data-02-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/paths-data-03-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/pservers-grad-09-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/pservers-grad-10-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/pservers-grad-11-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/pservers-grad-12-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/pservers-pattern-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/shapes-intro-01-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/struct-image-06-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/styling-css-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/styling-css-02-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/styling-css-03-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/text-intro-03-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/text-intro-04-t.svg [ Crash NeedsManualRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/text-text-06-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/types-basicDOM-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/filters/filterRegions.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/paints/patternRegions-positioned-objects.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/paints/patternRegions.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/smallFonts.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textAnchor.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textAnchor2.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textAnchor3.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textDecoration2.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textEffect.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textEffect2.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textEffect3.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textFeatures.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textLayout.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textLayout2.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textLength.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textOnPath.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textOnPath2.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textOnPath3.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textOnPathSpaces.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textPosition.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textPosition2.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textProperties.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/textProperties2.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/verticalText.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/verticalTextOnPath.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/batik/text/xmlSpace.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/carto.net/button.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/carto.net/colourpicker.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/carto.net/combobox.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/carto.net/scrollbar.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/carto.net/selectionlist.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/carto.net/slider.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/carto.net/tabgroup.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/carto.net/textbox.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/carto.net/window.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/altglyph.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/bug45331.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/clip-mask-negative-scale.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/coords-relative-units-transforms.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/dominant-baseline-hanging.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/foreign-object-skew.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/getscreenctm-in-mixed-content.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/glyph-selection-arabic-forms.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/glyph-selection-non-bmp.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/inline-svg-in-xhtml.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/junk-data.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/linking-a-03-b-preserveAspectRatio.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/linking-a-03-b-transform.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/linking-a-03-b-viewTarget.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/linking-a-03-b-zoomAndPan.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/load-non-wellformed.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/missing-xlink.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/object-sizing.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/path-bad-data.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/pattern-rotate-gaps.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/pattern-rotate.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/preserve-aspect-ratio-syntax.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/rootmost-svg-xy-attrs.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/stroked-pattern.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/style-attribute-font-size.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/svg-fonts-in-html.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/text-dom-01-f.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/text-match-highlight.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/use-dynamic-append.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/use-events-crash.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/use-font-face-crash.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/viewbox-syntax.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/hixie/error/002.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/hixie/error/012.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/hixie/mixed/003.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/hixie/mixed/009.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/text/append-text-node-to-tspan.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/text/font-size-below-point-five-2.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/text/foreignObject-text-clipping-bug.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/text/kerning.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/text/multichar-glyph.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/text/scaling-font-with-geometric-precision.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/text/textPathBoundsBug.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/transforms/text-with-mask-with-svg-transform.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/transforms/text-with-pattern-inside-transformed-html.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/transforms/text-with-pattern-with-svg-transform.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/wicd/test-rightsizing-a.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/wicd/test-rightsizing-b.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/wicd/test-scalable-background-image1.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/wicd/test-scalable-background-image2.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-background-images.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-coords-viewattr-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-hixie-mixed-009.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-img-preserveAspectRatio-support-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-replaced-intrinsic-ratio-001.htm [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-svg-through-object-with-absolute-size-2.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-svg-through-object-with-absolute-size.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-svg-through-object-with-auto-size.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-svg-through-object-with-huge-size.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-svg-through-object-with-override-size.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-svg-through-object-with-percentage-size.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/text/zoom-hixie-mixed-009.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/45621.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug10269-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug10296-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug1055-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug106158-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug10633.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug113235-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug113235-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug113424.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug1188.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug12384.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug13105.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug13118.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug1318.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug138725.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug139524-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug157890.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug16012.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug16252.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug17138.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug18359.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug19061-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug19061-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug194024.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug20579.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug22019.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug23151.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug23235.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug24200.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug2479-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug2479-3.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug26178.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug28928.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug29326.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug30559.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug30692.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug33855.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug39209.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug4093.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug43204.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug4382.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug4429.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug44505.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug4527.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug46368-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug46368-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug46623-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug48028-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug51037.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug51727.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug52505.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug52506.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug55545.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug57828-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug59354.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug60749.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug60804.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug60807.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug647.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug68912.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug7112-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug7112-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug7121-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug73321.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug7342.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug83786.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug8381.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug8858.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug8950.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug92647-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug96334.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug99948.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/collapsing_borders/bug41262-3.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/collapsing_borders/bug41262-4.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/core/bloomberg.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/core/margins.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/dom/tableDom.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/marvin/backgr_index.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/marvin/backgr_layers-opacity.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/marvin/backgr_position-table.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/marvin/backgr_simple-table-cell.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/marvin/backgr_simple-table-column-group.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/marvin/backgr_simple-table-column.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/marvin/backgr_simple-table-row-group.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/marvin/backgr_simple-table-row.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/marvin/backgr_simple-table.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/other/move_row.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/other/ms.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/other/slashlogo.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/other/test6.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/other/wa_table_thtd_rowspan.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/other/wa_table_tr_align.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug1055-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug1128.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug11331.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug1725.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug21518.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug22122.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug2479-5.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug45621.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug46268-4.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug58402-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug7121-2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug89315.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/bugs/bug92647-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/collapsing_borders/bug41262-5.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/collapsing_borders/bug41262-6.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_border-table-cell.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_border-table-column-group.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_border-table-column.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_border-table-quirks.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_border-table-row-group.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_border-table-row.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_border-table.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_fixed-bg.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_layers-hide.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_layers-show.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_position-table-cell.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_position-table-column-group.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_position-table-column.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_position-table-row-group.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/backgr_position-table-row.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/table_overflow_style_reflow_cell_sibling.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/table_overflow_style_reflow_row.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/marvin/table_overflow_style_reflow_row_sibling.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] transforms/3d/general/perspective-non-layer.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/basic/001.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/basic/011.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/basic/013.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/basic/generic-family-changes.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/basic/generic-family-reset.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/break-word.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/chromium-linux-fontconfig-renderstyle.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/drawBidiText.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/font-stretch.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/font-weight.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/international/bidi-listbox-atsui.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/international/bidi-listbox.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/international/bidi-menulist.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/international/hindi-spacing.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/international/khmer-selection.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/international/pop-up-button-text-alignment-and-direction.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/international/unicode-bidi-plaintext-in-textarea.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/international/unicode-bidi-plaintext.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/international/wrap-CJK-001.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/justify-ideograph-simple.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/justify-ideograph-vertical.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/large-text-composed-char.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/selection-hard-linebreak.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/selection-rect-rounding.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/sub-pixel/text-scaling-pixel.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/textIteratorNilRenderer.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/updateNewFont.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/wbr-pre.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/wbr-styled.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/whitespace/001.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/whitespace/019.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/whitespace/023.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/whitespace/024.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/whitespace/029.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/whitespace/030.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/whitespace/pre-wrap-overflow-selection.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/whitespace/pre-wrap-spaces-after-newline.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/word-break.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/compositedscrolling/scrollbars/custom-scrollbar-with-incomplete-style.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/compositedscrolling/scrollbars/listbox-scrollbar-combinations.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/compositedscrolling/scrollbars/overflow-scrollbar-combinations.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/gestures/gesture-tapHighlight-pixel-rotated-div.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/gestures/gesture-tapHighlight-pixel-rotated-link.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/gestures/gesture-tapHighlight-pixel-transparent.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/overflow/nested-render-surfaces-with-intervening-clip.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/overflow/nested-render-surfaces-with-rotation.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/overflow/nested-render-surfaces.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/video/video-controls-layer-creation.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/spelling/grammar-markers-hidpi.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/spelling/grammar-markers.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/spelling/inline-spelling-markers-hidpi-composited.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/spelling/inline-spelling-markers-hidpi.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/spelling/inline_spelling_markers.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/canvas/alpha.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/canvas/canvas-text-space-characters.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/events/scale-and-scroll-body.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/calendar-picker/calendar-picker-appearance-ar.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/calendar-picker/calendar-picker-appearance-minimum-date.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/calendar-picker/calendar-picker-appearance-required-ar.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/calendar-picker/calendar-picker-appearance-required.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/calendar-picker/calendar-picker-appearance-ru.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/calendar-picker/calendar-picker-appearance-step.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/calendar-picker/calendar-picker-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/calendar-picker/month-picker-appearance-step.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/calendar-picker/month-picker-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/calendar-picker/week-picker-appearance-step.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/calendar-picker/week-picker-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/color/color-suggestion-picker-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/color/color-suggestion-picker-one-row-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/color/color-suggestion-picker-two-row-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/color/color-suggestion-picker-with-scrollbar-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/datalist/input-appearance-range-with-datalist-zoomed.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/date/date-appearance-basic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/date/date-appearance-pseudo-elements.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/datetimelocal/datetimelocal-appearance-basic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/datetimelocal/datetimelocal-appearance-l10n.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/file/file-input-pressed-state.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/month/month-appearance-basic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/month/month-appearance-l10n.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/month/month-appearance-pseudo-elements.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/date-suggestion-picker-appearance-rtl.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/date-suggestion-picker-appearance-with-scroll-bar.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/date-suggestion-picker-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-locale-hebrew.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-rtl.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-with-scroll-bar.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/month-suggestion-picker-appearance-rtl.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/month-suggestion-picker-appearance-with-scroll-bar.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/month-suggestion-picker-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/time-suggestion-picker-appearance-locale-hebrew.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/time-suggestion-picker-appearance-rtl.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/time-suggestion-picker-appearance-with-scroll-bar.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/time-suggestion-picker-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/week-suggestion-picker-appearance-rtl.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/week-suggestion-picker-appearance-with-scroll-bar.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/suggestion-picker/week-suggestion-picker-appearance.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/time/time-appearance-basic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/time/time-appearance-pseudo-elements.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/week/week-appearance-basic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/week/week-appearance-pseudo-elements.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/images/12-55.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/images/182.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/images/2-dht.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/images/23-55.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/images/55.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/sub-pixel/selection/selection-rect-in-sub-pixel-table.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/atsui-small-caps-punctuation-size.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/whitespace/020.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] http/tests/media/video-buffered-range-contains-currentTime.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/controls-layout-direction.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] plugins/embed-attributes-style.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/as-background-image/svg-as-background-1.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/as-background-image/svg-as-background-5.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/as-background-image/svg-as-background-6.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/as-object/embedded-svg-immediate-offsetWidth-query.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/as-object/embedded-svg-size-changes.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/as-object/nested-embedded-svg-size-changes.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/use-instanceRoot-event-bubbling.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGLengthList-appendItem.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGLengthList-getItem.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGLengthList-initialize.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGLengthList-insertItemBefore.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGLengthList-removeItem.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGLengthList-replaceItem.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGLengthList-xml-dom-modifications.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGLocatable-getCTM-svg-root.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGPathSegList-appendItem.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGPathSegList-clear-and-initialize.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGPathSegList-insertItemBefore.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGPathSegList-removeItem.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGPathSegList-replaceItem.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGPathSegList-xml-dom-synchronization.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/SVGStringList-basics.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/dom/css-transforms.xhtml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/hixie/perf/001.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/hixie/perf/002.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/hixie/perf/003.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/hixie/perf/007.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/text/text-layout-crash.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/text/zoom-coords-viewattr-01-b.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug22513.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/atsui-multiple-renderers.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/atsui-small-caps-punctuation-size.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/capitalize-boundaries.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/emphasis-complex.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/emphasis.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/justify-ideograph-complex.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/whitespace/020.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/deferred/fast/images/12-55.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/deferred/fast/images/182.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/deferred/fast/images/23-55.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/deferred/fast/images/55.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/compositedscrolling/overflow/do-not-paint-outline-into-composited-scrolling-contents.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/compositedscrolling/overflow/nested-render-surfaces-with-intervening-clip.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/compositedscrolling/overflow/nested-render-surfaces-with-rotation.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/compositedscrolling/overflow/nested-render-surfaces.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/compositedscrolling/scrollbars/rtl/overflow-scroll-rtl.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/fast/canvas/alpha.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/fast/canvas/canvas-text-space-characters.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/geometry/layer-due-to-layer-children-deep-switch.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/geometry/layer-due-to-layer-children-switch.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/geometry/limit-layer-bounds-overflow-root.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/geometry/preserve-3d-switching.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/layer-creation/no-compositing-for-preserve-3d.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/overflow/selection-gaps-after-removing-scrolling-contents.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/overflow/selection-gaps-toggling-with-scrolling-contents.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/overflow/selection-gaps-toggling.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/squashing/no-squashing-into-another-clip-layer.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/video/video-poster.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] compositing/visibility/visibility-image-layers-dynamic.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css1/formatting_model/horizontal_formatting.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] css3/compositing/mix-blend-mode-composited-layers.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/caret/caret-height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/move-by-word-visually-mac.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] editing/selection/move-by-word-visually-multi-line.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/block/float/032.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/block/float/overhanging-tall-block.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/canvas/canvas-text-ideographic-space.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/acid2-pixel.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/acid2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/css/bidi-override-in-anonymous-block.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/dynamic/positioned-movement-with-positioned-children.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/focus-selection-textarea.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/hidden-listbox.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/text-control-intrinsic-widths.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/forms/textfield-overflow.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/frames/onlyCommentInIFrame.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/repaint/control-clip.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/repaint/multi-layout-one-frame.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/repaint/search-field-cancel.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/repaint/subtree-root-skipped.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/replaced/table-percent-height.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/table/table-all-rowspans-height-distribution-in-rows-except-overlapped.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/sub-pixel/text-scaling-ltr.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/sub-pixel/text-scaling-rtl.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/sub-pixel/text-scaling-vertical.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] fast/text/whitespace/pre-newline-box-test.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] http/tests/misc/acid2-pixel.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] http/tests/misc/acid2.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] media/video-paint-invalidation.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/W3C-SVG-1.1/struct-group-03-t.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/animations/svglength-animation-px-to-exs.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/custom/path-textPath-simulation.svg [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/hixie/error/013.xml [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/text/text-rescale.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-svg-as-background-with-relative-size-and-viewBox.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] svg/zoom/page/zoom-svg-as-background-with-relative-size.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla/bugs/bug2479-4.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] tables/mozilla_expected_failures/other/test4.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] touchadjustment/html-label.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/sub-pixel/text-scaling-ltr.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/sub-pixel/text-scaling-rtl.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/sub-pixel/text-scaling-vertical.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/antialiasedtext/fast/text/whitespace/pre-newline-box-test.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/compositedscrolling/overflow/selection-gaps-after-removing-scrolling-contents.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/compositedscrolling/overflow/selection-gaps-toggling-with-scrolling-contents.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/compositedscrolling/overflow/selection-gaps-toggling.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/gpu/fast/canvas/canvas-text-ideographic-space.html [ NeedsRebaseline ]
+crbug.com/395425 [ Win7 ] virtual/threaded/compositing/visibility/visibility-image-layers-dynamic.html [ NeedsRebaseline ]
+
# Ahem font is sometimes not loaded on Windows.
crbug.com/392046 [ Win ] fast/inline/justify-emphasis-inline-box.html [ Pass Failure ]
crbug.com/392046 [ Win ] fast/text/whitespace/002.html [ Pass Failure ]
crbug.com/397321 compositing/repaint/should-not-repaint-composited-opacity.html [ Crash Pass ]
crbug.com/397321 svg/custom/pattern-3-step-cycle.html [ Crash Pass ]
crbug.com/397321 svg/filters/filter-refresh.svg [ Crash Pass ]
-crbug.com/397321 virtual/softwarecompositing/repaint/repaint-into-ancestor-after-layout.html [ Crash Pass ]
-crbug.com/399507 inspector/timeline/tracing/update-layer-tree.html [ Failure Pass ]
-crbug.com/399507 virtual/deferred/inspector/timeline/tracing/update-layer-tree.html [ Failure Pass ]
-crbug.com/399507 virtual/implsidepainting/inspector/timeline/tracing/update-layer-tree.html [ Failure Pass ]
+crbug.com/399507 inspector/tracing/update-layer-tree.html [ Failure Pass ]
+crbug.com/399507 virtual/deferred/inspector/tracing/update-layer-tree.html [ Failure Pass ]
+crbug.com/399507 virtual/implsidepainting/inspector/tracing/update-layer-tree.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] http/tests/inspector/tracing/timeline-xhr-event.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] inspector/tracing/timeline-event-dispatch.html [ Failure Pass ]
+crbug.com/407915 [ Release ] virtual/deferred/inspector/tracing/timeline-event-dispatch.html [ Failure Pass ]
+crbug.com/407915 virtual/implsidepainting/inspector/tracing/timeline-event-dispatch.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] inspector/tracing/timeline-layout-reason.html [ Failure Pass ]
+crbug.com/407915 [ Win Mac Release ] inspector/tracing/timeline-parse-html.html [ Failure Pass ]
+crbug.com/407915 [ Win Mac Release ] virtual/deferred/inspector/tracing/timeline-parse-html.html [ Failure Pass ]
+crbug.com/407915 [ Win Mac ] virtual/implsidepainting/inspector/tracing/timeline-parse-html.html [ Failure Pass ]
+crbug.com/407915 [ Release ] inspector/tracing/timeline-script-tag-1.html [ Failure Pass ]
+crbug.com/407915 [ Release ] virtual/deferred/inspector/tracing/timeline-script-tag-1.html [ Failure Pass ]
+crbug.com/407915 [ Release ] virtual/implsidepainting/inspector/tracing/timeline-script-tag-1.html [ Failure Pass ]
+crbug.com/407915 [ Release ] inspector/tracing/decode-resize.html [ Failure Pass Timeout ]
+crbug.com/407915 [ Win Release ] http/tests/inspector/tracing/timeline-xhr-response-type-blob-event.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] inspector/tracing/timeline-injected-script-eval.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] inspector/tracing/timeline-layout.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] inspector/tracing/timeline-recalculate-styles.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] inspector/tracing/timeline-time.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] virtual/deferred/inspector/tracing/decode-resize.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] virtual/deferred/inspector/tracing/timeline-injected-script-eval.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] virtual/deferred/inspector/tracing/timeline-layout-reason.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] virtual/deferred/inspector/tracing/timeline-layout.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] virtual/deferred/inspector/tracing/timeline-recalculate-styles.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] virtual/deferred/inspector/tracing/timeline-time.html [ Failure Pass Timeout ]
+crbug.com/407915 virtual/implsidepainting/inspector/timeline/timeline-frames.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] virtual/implsidepainting/inspector/tracing/timeline-injected-script-eval.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] virtual/implsidepainting/inspector/tracing/timeline-layout.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] virtual/implsidepainting/inspector/tracing/timeline-recalculate-styles.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] virtual/implsidepainting/inspector/tracing/timeline-time.html [ Failure Pass ]
+crbug.com/407915 [ Mac Release ] virtual/implsidepainting/inspector/tracing/timeline-timer.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] virtual/implsidepainting/inspector/tracing/paint-command-log-nodes.html [ Failure Pass ]
+crbug.com/407915 [ Win Release ] virtual/implsidepainting/inspector/tracing/timeline-layout-reason.html [ Failure Pass ]
+crbug.com/409580 inspector/device-emulation/device-emulation-320-2x.html [ Crash Pass ]
+crbug.com/407993 [ Win Release ] media/encrypted-media/encrypted-media-waiting-for-a-key.html [ Pass Timeout ]
+crbug.com/407993 [ Win Release ] media/encrypted-media/encrypted-media-v2-events.html [ Pass Timeout ]
+crbug.com/407993 [ Mac Win Release ] media/encrypted-media/encrypted-media-playback-multiple-sessions.html [ Pass Timeout ]
+crbug.com/407993 [ Win Release ] media/encrypted-media/encrypted-media-playback-setmediakeys-before-src.html [ Timeout Pass ]
+crbug.com/407993 [ Win ] media/encrypted-media/prefixed/encrypted-media-events.html [ Crash Pass ]
+crbug.com/409579 http/tests/security/javascriptURL/xss-DENIED-to-javascript-url-in-foreign-domain-subframe.html [ Pass Timeout ]
+#crbug.com/409715 [ Win ] svg/W3C-SVG-1.1/text-intro-04-t.svg [ Crash Pass ]
+crbug.com/409713 [ Linux Win Release ] fast/dom/Window/window-early-properties-xhr.html [ Pass Timeout ]
+crbug.com/409712 http/tests/filesystem/workers/resolve-url.html [ Pass Timeout ]
+crbug.com/409767 crypto/worker-subtle-crypto-concurrent.html [ Crash Pass Timeout ]
crbug.com/357462 http/tests/security/contentSecurityPolicy/1.1/frame-ancestors/frame-ancestors-nested-cross-in-cross-none-block.html [ Failure Pass Timeout ]
crbug.com/357462 http/tests/security/contentSecurityPolicy/1.1/frame-ancestors/frame-ancestors-nested-cross-in-cross-self-block.html [ Failure Pass Timeout ]
--- /dev/null
+Ensure that if we have an AXNodeObject with a raw pointer to a Node, the accessible object gets correctly detached if we adopt the node into a new document.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE html>
+<script src="../resources/js-test.js"></script>
+
+<!-- Nodes inside a canvas get an AXNodeObject, not an AXRenderObject. -->
+<canvas>
+ <div id=node2></div>
+</canvas>
+
+<script>
+ description("Ensure that if we have an AXNodeObject with a raw pointer to a Node, the accessible object gets correctly detached if we adopt the node into a new document.");
+
+ // This triggers an asynchronous accessibility notification.
+ document.getElementById("node2").setAttribute("aria-label", "Label");
+
+ // Adopt the node into a new document, then garbage-collect it.
+ // Make sure the notification doesn't try to access the invalid node.
+ document.implementation.createDocument("", null).adoptNode(node2);
+ gc();
+</script>
--- /dev/null
+Heading
+
+Makes sure that accessing the word boundaries of an AXStaticText object doesn't cause a crash when it has an inline text box of length zero.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Word start for index -1: 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<html>
+<head>
+<script src="../resources/js-test.js"></script>
+<style>
+ h1:first-letter {
+ text-transform:uppercase;
+ }
+</style>
+</head>
+<body>
+
+ <h1 id="heading">
+ heading
+ </h1>
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+description("Makes sure that accessing the word boundaries of an AXStaticText object doesn't cause a crash when it has an inline text box of length zero.");
+if (window.accessibilityController) {
+ var axHeading = accessibilityController.accessibleElementById('heading');
+ var axStaticText = axHeading.childAtIndex(0);
+ debug('Word start for index -1: ' + axStaticText.wordStart(-1));
+}
+</script>
+</body>
+</html>
--- /dev/null
+{
+ "bounds": [800, 600],
+ "children": [
+ {
+ "bounds": [800, 600],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "children": [
+ {
+ "position": [8, 8],
+ "bounds": [100, 100],
+ "children": [
+ {
+ "bounds": [100, 100],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "backgroundColor": "#D3D3D3",
+ "repaintRects": [
+ [0, 0, 100, 100]
+ ],
+ "children": [
+ {
+ "bounds": [100, 100],
+ "shouldFlattenTransform": false,
+ "transform": [
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, -0.05],
+ [0, 0, 0, 1]
+ ],
+ "children": [
+ {
+ "bounds": [50, 50],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "backgroundColor": "#FFFF00",
+ "transform": [
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 0, 1, 1]
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+<!DOCTYPE html>
+<div id="target" style="transform: translateZ(0); width: 100px; height: 100px; background-color: lightblue">
+ <div style="perspective: 20px; width: 100px; height: 100px; background-color: lightgray">
+ <div style="width: 50px; height: 50px; transform: translateZ(1px); background-color: yellow">
+ </div>
+ </div>
+</div>
+<script src="../fast/repaint/resources/text-based-repaint.js"></script>
+<script>
+// Tests that transitioning the container div from "composited but paints into ancestor" into "composited into own backing"
+// for ancestor reasons (in this case due to opacity change) correctly invalidates the old paint invalidation backing (document)
+// before the change. In this case, the reason it can no longer paint into its ancestor is because the ancestor has no visible content
+// (since the background color is removed).
+function repaintTest() {
+ target.style.backgroundColor = '';
+}
+runRepaintTest()
+</script>
\ No newline at end of file
--- /dev/null
+{
+ "bounds": [800, 600],
+ "children": [
+ {
+ "bounds": [800, 600],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "repaintRects": [
+ [8, 8, 784, 100]
+ ],
+ "children": [
+ {
+ "position": [8, 8],
+ "bounds": [784, 100],
+ "opacity": 0,
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "backgroundColor": "#FF0000",
+ "repaintRects": [
+ [0, 0, 784, 100],
+ [0, 0, 784, 100]
+ ],
+ "children": [
+ {
+ "bounds": [784, 100],
+ "shouldFlattenTransform": false,
+ "transform": [
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, -1],
+ [0, 0, 0, 1]
+ ],
+ "children": [
+ {
+ "bounds": [100, 100],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "backfaceVisibility": "hidden",
+ "backgroundColor": "#0000FF"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+<!DOCTYPE html>
+<style>
+.spinner {
+ backface-visibility: hidden;
+ background-color: blue;
+ height: 100px;
+ width: 100px;
+}
+.transparent {
+ opacity: 0;
+}
+#container {
+ -webkit-perspective: 1px;
+ background-color: red;
+ overflow: hidden;
+}
+</style>
+<script src="../fast/repaint/resources/text-based-repaint.js"></script>
+<div id="container">
+ <div class="spinner"></div>
+</div>
+<script>
+// Tests that transitioning the container div from "composited but paints into ancestor" into "composited into own backing"
+// for intrinsic reasons (in this case due to opacity change) correctly invalidates the old paint invalidation backing (document)
+// before the change.
+function repaintTest() {
+ document.getElementById('container').classList.add('transparent');
+}
+if (window.testRunner)
+ runRepaintTest();
+</script>
"drawsContent": true,
"children": [
{
- "children": [
- {
- "position": [205, 205],
- "transformOrigin": [175, 175],
- "bounds": [225, 225],
- "drawsContent": true,
- "backgroundColor": "#000000"
- },
- {
- "bounds": [225, 225],
- "drawsContent": true
- }
- ]
+ "position": [205, 205],
+ "transformOrigin": [175, 175],
+ "bounds": [225, 225],
+ "drawsContent": true,
+ "backgroundColor": "#000000"
+ },
+ {
+ "bounds": [225, 225],
+ "drawsContent": true,
+ "backgroundColor": "#008000"
}
]
}
"drawsContent": true,
"children": [
{
- "children": [
- {
- "position": [105, 105],
- "bounds": [100, 100],
- "contentsOpaque": true,
- "drawsContent": true,
- "backgroundColor": "#000000"
- },
- {
- "bounds": [125, 125],
- "drawsContent": true
- }
- ]
+ "position": [105, 105],
+ "bounds": [100, 100],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "backgroundColor": "#000000"
+ },
+ {
+ "bounds": [125, 125],
+ "drawsContent": true,
+ "backgroundColor": "#008000"
}
]
}
"drawsContent": true,
"children": [
{
- "children": [
- {
- "position": [105, 105],
- "transformOrigin": [75, 75],
- "bounds": [125, 125],
- "drawsContent": true,
- "backgroundColor": "#000000"
- },
- {
- "bounds": [125, 125],
- "drawsContent": true
- }
- ]
+ "position": [105, 105],
+ "transformOrigin": [75, 75],
+ "bounds": [125, 125],
+ "drawsContent": true,
+ "backgroundColor": "#000000"
+ },
+ {
+ "bounds": [125, 125],
+ "drawsContent": true,
+ "backgroundColor": "#008000"
}
]
}
--- /dev/null
+<!DOCTYPE html>
+<script src="../../resources/js-test.js"></script>
+<script src="resources/link-highlight-helper.js"></script>
+<link rel="stylesheet" type="text/css" href="resources/link-highlight-style.css">
+<style>
+#highlightTarget {
+ position: absolute;
+ height: 200px;
+ left: 50px;
+ top: 180px;
+ width: 20px;
+ background-color: red;
+}
+
+#clip {
+ position: absolute;
+ top: 200px;
+ left: 0;
+ width: 50px;
+ height: 150px;
+ overflow: hidden;
+}
+
+#fakeTarget {
+ width: 150px;
+ height: 150px;
+ background-color: lightblue;
+}
+
+</style>
+
+<div id=highlightTarget class=target></div>
+<div id=clip>
+ <div id=fakeTarget class=target></div>
+</div>
+
+<script>
+description('This test is successful if the red box is completely covered in a green rectangle with square corners.');
+createHighlight(highlightTarget);
+testRunner.dumpAsTextWithPixelResults();
+</script>
--- /dev/null
+<!DOCTYPE html>
+<script src="../../resources/js-test.js"></script>
+<script src="resources/link-highlight-helper.js"></script>
+<link rel="stylesheet" type="text/css" href="resources/link-highlight-style.css">
+<style>
+#highlightTarget {
+ position: absolute;
+ height: 200px;
+ left: 50px;
+ top: 180px;
+ width: 20px;
+ background-color: red;
+}
+
+#clip {
+ position: absolute;
+ top: 200px;
+ left: 0;
+ width: 50px;
+ height: 150px;
+ overflow: hidden;
+}
+
+#fakeTarget {
+ width: 150px;
+ height: 150px;
+ background-color: lightblue;
+}
+
+</style>
+
+<div id=highlightTarget class=target></div>
+<div id=clip>
+ <div id=fakeTarget class=target></div>
+</div>
+
+<script>
+description('This test is successful if the red box is completely covered in a green rectangle with square corners.');
+onload = testHighlightTarget;
+</script>
internals.settings.setMockGestureTapHighlightsEnabled(true);
}
+function testHighlightTarget(id) {
+ useMockHighlight();
+
+ var clientRect = document.getElementById('highlightTarget').getBoundingClientRect();
+ x = (clientRect.left + clientRect.right) / 2;
+ y = (clientRect.top + clientRect.bottom) / 2;
+ if (window.testRunner) {
+ testRunner.dumpAsTextWithPixelResults();
+ testRunner.waitUntilDone();
+ }
+
+ if (window.eventSender) {
+ eventSender.gestureTapDown(x, y, 30, 30);
+ eventSender.gestureShowPress(x, y, 30, 30);
+ window.setTimeout(function() { window.testRunner.notifyDone(); }, 0);
+ } else {
+ debug("This test requires eventSender");
+ }
+}
-webkit-tap-highlight-color: rgb(0, 255, 0);
}
+.target {
+ cursor: pointer; /* enables link highlighting */
+ -webkit-tap-highlight-color: rgb(0, 255, 0);
+ border: 2px solid grey;
+ box-sizing: border-box;
+}
+/* touch adjustment candidate */
+.target:active {
+ border-color: red;
+}
+
.transparentHighlight {
-webkit-tap-highlight-color: rgba(0, 255, 0, 0.5);
}
--- /dev/null
+<!DOCTYPE html>
+<!-- This test is to catch a flakey use-after-free for ASAN bots. (see crbug.com/402407) -->
+<script>
+ function start() {
+ svgIframe = document.createElement('iframe');
+ svgIframe.src = 'resources/do-not-crash-use-after-free-update-widget-positions.svg';
+
+ bodyElement = document.body;
+
+ articleElement = document.createElement('article');
+ acronymElement = document.createElement('acronym');
+
+ bodyElement.style.overflow = 'scroll';
+
+ selectionRange = document.createRange();
+ selectionRange.selectNodeContents(articleElement);
+ selectionRange.surroundContents(bodyElement);
+
+ w3Iframe = document.createElementNS('http://www.w3.org/1999/xhtml', 'iframe');
+ w3Iframe.src = 'resources/do-not-crash-use-after-free-update-widget-positions-iframe.html';
+ w3Iframe.onload = iframeOnLoad;
+ document.documentElement.appendChild(w3Iframe);
+
+ selectionRange.insertNode(svgIframe);
+ bodyElement.style.position = 'fixed';
+
+ while (svgIframe.parentNode) {
+ svgIframe = svgIframe.parentNode;
+ }
+
+ svgIframe.createElement;
+
+ document.documentElement.appendChild(svgIframe);
+
+ acronymElement.style.position = 'absolute';
+ }
+
+ function iframeOnLoad() {
+ bodyElement.appendChild(acronymElement);
+ acronymElement.offsetWidth;
+
+ acronymElement.appendChild(w3Iframe);
+ w3Iframe.offsetWidth;
+
+ window.setTimeout('window.iframeCallback()', 50);
+ }
+
+ function iframeCallback() {
+ bodyElement.style.cssText = null;
+
+ w3Iframe.contentDocument.location.hash = 'element0';
+ }
+
+ if (window.testRunner) {
+ testRunner.dumpAsText();
+ }
+</script>
+<body onload = "start()">
+ This test passes if it doesn't crash.
+</body>
--- /dev/null
+<!DOCTYPE html>
+<body id="element0">
+</body>
+
--- /dev/null
+<?xml version="1.0"?>
+<svg id="element0" xmlns="http://www.w3.org/2000/svg">
+ <rect>
+ <animate from="10px" to="100px" />
+ <animate attributeName="height" to="10px" dur="10s" />
+ </rect>
+</svg>
--- /dev/null
+No border radius (should be using composited scrolling): Pass.
+Has border radius (should not be using composited scrolling): Pass.
+
--- /dev/null
+<!DOCTYPE HTML>
+<style>
+#scroller {
+ overflow: scroll;
+ height: 300px;
+ width: 300px;
+ background-color: red;
+}
+
+#scrolled {
+ height: 1000px;
+ width: 250px;
+ background-color: green;
+}
+
+#fixed {
+ position: fixed;
+ height: 100px;
+ width: 100px;
+ background-color: green;
+ top: 400px;
+ left: 100px;
+}
+</style>
+<div id="scroller">
+ <div id="scrolled"></div>
+ <div id="fixed"></div>
+</div>
+<script>
+function isUsingCompositedScrolling(layer) {
+ if (layer.bounds[1] == 1000)
+ return true;
+ if (layer.children) {
+ for (var i = 0; i < layer.children.length; i++) {
+ if (isUsingCompositedScrolling(layer.children[i]))
+ return true;
+ }
+ }
+ return false;
+}
+
+if (window.internals)
+ window.internals.settings.setPreferCompositingToLCDTextEnabled(true);
+
+if (window.testRunner) {
+ window.testRunner.dumpAsText();
+ window.testRunner.waitUntilDone();
+}
+
+var result = "";
+
+onload = function() {
+ if (window.internals) {
+ result += "No border radius (should be using composited scrolling): ";
+ if (isUsingCompositedScrolling(JSON.parse(window.internals.layerTreeAsText(document))))
+ result += "Pass.\n";
+ else
+ result += "Fail.\n"
+ }
+ document.getElementById("scroller").style.borderRadius = '5px';
+ requestAnimationFrame(function() {
+ if (window.internals) {
+ result += "Has border radius (should not be using composited scrolling): ";
+ if (!isUsingCompositedScrolling(JSON.parse(window.internals.layerTreeAsText(document))))
+ result += "Pass.\n";
+ else
+ result += "Fail.\n"
+ }
+
+ if (window.testRunner) {
+ window.testRunner.setCustomTextOutput(result);
+ window.testRunner.notifyDone();
+ }
+ });
+};
+</script>
--- /dev/null
+{
+ "bounds": [800, 600],
+ "children": [
+ {
+ "bounds": [800, 600],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "children": [
+ {
+ "position": [8, 8],
+ "bounds": [500, 500],
+ "drawsContent": true,
+ "repaintRects": [
+ [0, 0, 500, 500],
+ [0, 0, 500, 500]
+ ],
+ "children": [
+ {
+ "bounds": [485, 485],
+ "children": [
+ {
+ "bounds": [5000, 5000],
+ "shouldFlattenTransform": false,
+ "drawsContent": true,
+ "repaintRects": [
+ [2000, 2000, 3000, 3000],
+ [0, 0, 5000, 5000]
+ ],
+ "children": [
+ {
+ "shouldFlattenTransform": false
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "children": [
+ {
+ "position": [0, 485],
+ "bounds": [485, 15],
+ "drawsContent": true,
+ "repaintRects": [
+ [0, 0, 485, 15]
+ ]
+ },
+ {
+ "position": [485, 0],
+ "bounds": [15, 485],
+ "drawsContent": true,
+ "repaintRects": [
+ [0, 0, 15, 485]
+ ]
+ },
+ {
+ "position": [485, 485],
+ "bounds": [15, 15],
+ "drawsContent": true
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+<!DOCTYPE HTML>
+<script src="../../fast/repaint/resources/text-based-repaint.js"></script>
+<script>
+if (window.internals)
+ internals.settings.setPreferCompositingToLCDTextEnabled(true);
+function repaintTest() {
+ document.getElementById('content').style.backgroundColor = 'green';
+ var container = document.getElementById('container');
+ container.scrollLeft = 2000;
+ container.scrollTop = 2000;
+}
+window.onload = runRepaintTest;
+</script>
+<style>
+#container {
+ width: 500px;
+ height: 500px;
+ overflow: scroll;
+}
+#content {
+ width: 5000px;
+ height: 5000px;
+ background-color: red;
+}
+</style>
+<div id="container">
+ <div id="content">
+ Tests invalidation of scrolling layer. Passes if the repaint rect is not clipped,
+ and there is no red when the scrolling container is scrolled.<br>
+ Note for manual testing: must run with --enable-prefer-compositing-to-lcd-text
+ on non-high-dpi machines to enable composited scrolling.
+ </div>
+</div>
--- /dev/null
+{
+ "bounds": [5008, 5016],
+ "children": [
+ {
+ "bounds": [5008, 5016],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "repaintRects": [
+ [8, 8, 5000, 5000]
+ ]
+ }
+ ]
+}
+
--- /dev/null
+<!DOCTYPE HTML>
+<script src="../../fast/repaint/resources/text-based-repaint.js"></script>
+<script>
+if (window.internals)
+ internals.settings.setPreferCompositingToLCDTextEnabled(true);
+function repaintTest() {
+ document.getElementById('content').style.backgroundColor = 'green';
+ window.scrollTo(2000, 2000);
+}
+window.onload = runRepaintTest;
+</script>
+<style>
+#content {
+ width: 5000px;
+ height: 5000px;
+ background-color: red;
+}
+</style>
+<div id="content">
+Tests invalidation of scrolling layer. Passes if the repaint rect is not clipped,
+and there is no red when the scrolling container is scrolled.<br>
+Note for manual testing: must run with --enable-prefer-compositing-to-lcd-text
+on non-high-dpi machines to enable composited scrolling.
+</div>
"bounds": [800, 600],
"contentsOpaque": true,
"drawsContent": true,
+ "repaintRects": [
+ [0, 0, 200, 200]
+ ],
"children": [
{
"children": [
--- /dev/null
+{
+ "bounds": [800, 600],
+ "children": [
+ {
+ "bounds": [800, 600],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "children": [
+ {
+ "position": [8, 8],
+ "bounds": [100, 100],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "backgroundColor": "#ADD8E6"
+ },
+ {
+ "children": [
+ {
+ "position": [-5, -5],
+ "bounds": [62, 62],
+ "drawsContent": true,
+ "backgroundColor": "#D3D3D3"
+ },
+ {
+ "position": [50, 50],
+ "bounds": [50, 50],
+ "drawsContent": true
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+<!DOCTYPE html>
+<style>
+.trysquashed {
+ width: 50px; height: 50px; background: lightgray
+}
+</style>
+<div style="width: 100px; height: 100px; transform: translateZ(0); background: lightblue"></div>
+<div class="trysquashed" style="position: absolute; top: 0px; left: 0px; -webkit-filter: drop-shadow(1px 1px 2px #000)"></div>
+<div class="trysquashed" style="position: absolute; top: 50px; left: 50px;"></div>
+
+<pre id="layers"></pre>
+<script>
+// Tests that layers with filters are not squashed.
+if (window.testRunner)
+ window.testRunner.dumpAsText();
+var layersResult = document.getElementById('layers');
+if (window.internals)
+ layersResult.innerText = window.internals.layerTreeAsText(document);
+</script>
\ No newline at end of file
[0, 200, 200, 100],
[0, 100, 200, 100],
[0, 100, 200, 100],
+ [0, 0, 200, 100],
[0, 0, 200, 100]
],
"children": [
[0, 200, 200, 100],
[0, 200, 200, 100],
[0, 100, 200, 100],
+ [0, 0, 200, 100],
[0, 0, 200, 100]
],
"children": [
--- /dev/null
+{
+ "bounds": [800, 600],
+ "children": [
+ {
+ "bounds": [800, 600],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "children": [
+ {
+ "position": [8, 8],
+ "children": [
+ {
+ "shouldFlattenTransform": false,
+ "transform": [
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, -0.001],
+ [0, 0, 0, 1]
+ ],
+ "children": [
+ {
+ "bounds": [200, 200],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "backgroundColor": "#00008B",
+ "transform": [
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 74, 200, 1]
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "position": [8, 8]
+ }
+ ]
+ }
+ ]
+}
+
--- /dev/null
+<!DOCTYPE HTML>
+<!--
+ This test ensures that the offset from renderer is correctly applied
+ to reparented overflow controls, even if they are unclipped.
+-->
+<style>
+#scroller {
+ overflow: scroll;
+ width: 300px;
+ height: 300px;
+ position: relative;
+ top: 10px;
+}
+
+#fixed {
+ position: fixed;
+ background: blue;
+ left: 90px;
+ width: 10px;
+ height: 10px;
+}
+
+#scrolled {
+ position: relative;
+ background: green;
+ width: 80px;
+ height: 500px;
+}
+</style>
+<div style="z-index: 1; position: absolute"></div>
+<div style="z-index: 0; perspective: 1000px; position: absolute;">
+ <div id='scroller'>
+ <div id='fixed'></div>
+ <div id='scrolled'></div>
+ </div>
+</div>
+<script>
+if (window.internals) {
+ window.internals.settings.setOverlayScrollbarsEnabled(true);
+ window.internals.settings.setPreferCompositingToLCDTextEnabled(true);
+ window.internals.settings.setLayerSquashingEnabled(false);
+}
+</script>
--- /dev/null
+<!DOCTYPE HTML>
+<!--
+ This test ensures that the offset from renderer is correctly applied
+ to reparented overflow controls, even if they are unclipped.
+-->
+<style>
+#scroller {
+ overflow: scroll;
+ width: 300px;
+ height: 300px;
+ position: relative;
+ top: 10px;
+}
+
+#fixed {
+ position: fixed;
+ background: blue;
+ left: 90px;
+ width: 10px;
+ height: 10px;
+}
+
+#scrolled {
+ position: relative;
+ background: green;
+ width: 80px;
+ height: 500px;
+}
+</style>
+<div style="z-index: 1; position: absolute"></div>
+<div style="z-index: 0; perspective: 1000px; position: absolute;">
+ <div id='scroller'>
+ <div id='fixed'></div>
+ <div id='scrolled'></div>
+ </div>
+</div>
+<script>
+// Check that a case with a reparented overflow control and a containing perspective node
+// paints the same with and without layer squashing.
+if (window.internals) {
+ window.internals.settings.setOverlayScrollbarsEnabled(true);
+ window.internals.settings.setPreferCompositingToLCDTextEnabled(true);
+ window.internals.settings.setLayerSquashingEnabled(true);
+}
+</script>
--- /dev/null
+<!DOCTYPE html>
+
+<div style="z-index: 1; position: absolute"></div>
+<div style="z-index: 0; perspective: 1000px; position: absolute;">
+ <div style="position: absolute; width: 200px; height: 200px; background-color: darkBlue; transform: translate3d(0px, 74px, 200px);">
+ </div>
+</div>
+<script>
+// Tests that a squashed layer that is the child of an element with perspective on it uses that element as its transform ancestor.
+
+if (window.internals)
+ internals.settings.setLayerSquashingEnabled(true);
+if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.setCustomTextOutput(window.internals.layerTreeAsText(document));
+}
+</script>
\ No newline at end of file
"drawsContent": true,
"children": [
{
- "children": [
- {
- "position": [250, 250],
- "bounds": [100, 100],
- "contentsOpaque": true,
- "drawsContent": true,
- "backgroundColor": "#0000FF"
- },
- {
- "position": [100, 100],
- "bounds": [200, 200],
- "drawsContent": true
- }
- ]
+ "position": [250, 250],
+ "bounds": [100, 100],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "backgroundColor": "#0000FF"
+ },
+ {
+ "position": [100, 100],
+ "bounds": [200, 200],
+ "drawsContent": true,
+ "backgroundColor": "#000000"
}
]
}
"drawsContent": true,
"children": [
{
- "children": [
- {
- "position": [250, 250],
- "bounds": [100, 100],
- "contentsOpaque": true,
- "drawsContent": true,
- "backgroundColor": "#0000FF"
- },
- {
- "position": [100, 100],
- "bounds": [200, 200],
- "drawsContent": true
- }
- ]
+ "position": [250, 250],
+ "bounds": [100, 100],
+ "contentsOpaque": true,
+ "drawsContent": true,
+ "backgroundColor": "#0000FF"
+ },
+ {
+ "position": [100, 100],
+ "bounds": [200, 200],
+ "drawsContent": true,
+ "backgroundColor": "#000000"
}
]
}
--- /dev/null
+Ensure that collapse with null clears selection
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS selection.rangeCount is 1
+PASS selection.rangeCount is 0
+PASS selection.rangeCount is 1
+PASS selection.rangeCount is 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+hello
--- /dev/null
+<!DOCTYPE HTML>
+<html>
+<body>
+<script src="../../resources/js-test.js"></script>
+
+<div id="div" contenteditable="true">hello</div>
+<script>
+description("Ensure that collapse with null clears selection");
+
+var selection = getSelection();
+selection.selectAllChildren(div);
+ selection.rangeCount
+ shouldBe("selection.rangeCount", "1");
+ selection.collapse(null);
+ shouldBe("selection.rangeCount", "0");
+ selection.collapse(div.firstChild, 2);
+ shouldBe("selection.rangeCount", "1");
+ selection.collapse(null);
+ shouldBe("selection.rangeCount", "0");
+</script>
+
+</body>
+</html>
<script>
description("When drawing an animated image to a canvas, the poster frame (or the first frame) should be printed.<br/>This test passes if the canvas is filled with the color rgb(64, 4, 30).");
+ if (window.testRunner) {
+ testRunner.waitUntilDone();
+ }
+
+
function ready() {
var canvas = document.getElementById("canvas");
var image = document.getElementById("image");
var canvasContext = canvas.getContext("2d");
- canvasContext.drawImage(image, 0, 0);
+ window.setTimeout(function() {
+
+ canvasContext.drawImage(image, 0, 0);
+
+ imageData = canvasContext.getImageData(0, 0, 1, 1);
+
+ shouldBe("imageData.data[0]", "64");
+ shouldBe("imageData.data[1]", "4");
+ shouldBe("imageData.data[2]", "30");
- imageData = canvasContext.getImageData(0, 0, 1, 1);
+ if (window.testRunner)
+ testRunner.notifyDone();
- shouldBe("imageData.data[0]", "64");
- shouldBe("imageData.data[1]", "4");
- shouldBe("imageData.data[2]", "30");
+ }, 200);
}
</script>
--- /dev/null
+Test 1 - This is a H1 heading.
+
+Test 2 - This is a simple paragraph.
+
+Test 3 - This is a paragraph with a nested element.
+
+Test 4 - This is a paragraph with a nested element that has a border.
+
+Test 5 - This is a transformed paragraph with a nested element that has a border.
+
+Test 6 - This is a transformed paragraph with a nested element that has a border.
+And then a second line.
+
+Test 7 - This is a paragraph inside something that does not have a compositing layer.
+
+Test 8 - This is raw text inside something that does not have a compositing layer.
+Test 9 - This is raw text inside something that has a compositing layer.
+Test 10 - This is raw text inside something that does not have a compositing layer.
+Test 11 - This is a rotated and scaled paragraph
+
+Test 12 - This is a rotated and scaled paragraph with a nested element that has a border.
+
+Test 13 - This is a paragraph with a nested element that has a border.
+
+This test exercises the webkitConvertPointFromNodeToPage() function
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Test parameter passing - should not crash
+PASS Missing parameter test
+Test did not crash and therefore was successful
+
+PASS null parameter test a
+Test did not crash and therefore was successful
+
+PASS null parameter test b
+Test did not crash and therefore was successful
+
+Test 1
+PASS x is 8
+PASS y is 13
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+PASS y is 53
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 2
+PASS x is 8
+PASS y is 51
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+PASS y is 91
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 3
+PASS x is 8
+PASS y is 85
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+PASS y is 125
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 4
+PASS x is 8
+PASS y is 119
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+PASS y is 159
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 5
+PASS x is 28
+PASS y is 153
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+PASS y is 193
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 6
+PASS x is 28
+PASS y is 187
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+PASS y is 227
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 7
+PASS x is 8
+PASS y is 239
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+PASS y is 279
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 8
+PASS x is 8
+PASS y is 273
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+PASS y is 313
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 9
+PASS x is 28
+PASS y is 291
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+PASS y is 331
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 10
+PASS x is 28
+PASS y is 309
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+PASS y is 349
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 11
+PASS x is 158
+FAIL y should be 376. Was 355.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 174
+FAIL y should be 394. Was 373.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 12
+PASS x is 168
+FAIL y should be 451. Was 428.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 184
+FAIL y should be 469. Was 446.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 13
+PASS x is 28
+PASS y is 487
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+PASS y is 527
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+ <head>
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <title>Test of webkitConvertPointFromNodeToPage function</title>
+ <script src="../../../resources/js-test.js"></script>
+ <style type="text/css" media="screen">
+ body {
+ height: 1000px;
+ }
+
+ h1 {
+ font-size: 14pt;
+ }
+ .layer {
+ position: relative;
+ width: 600px;
+ height: 500px;
+ -webkit-transform: translate(0px);
+ }
+ .dot {
+ position: absolute;
+ background-color: #0f0;
+ width: 10px;
+ height: 10px;
+ left: 0px;
+ top: 0px;
+ visibility: hidden;
+ }
+ b.border {
+ border: 5px solid blue;
+ }
+
+ .a {
+ position: relative;
+ left: 10px;
+ background: #cbc;
+ width: 800px;
+ -webkit-transform-origin: top left;
+ -webkit-transform: translate(130px, 20px) rotate(-35deg) scale(0.6);
+ padding: 5px;
+ margin-bottom: 20px;
+ }
+ .b {
+ position: relative;
+ background: #bcc;
+ -webkit-transform: translate(20px);
+ }
+ .c {
+ position: relative;
+ -webkit-transform: translate(20px);
+ background: #99c;
+ }
+ .d {
+ position: relative;
+ background: #cc9;
+ }
+
+ </style>
+ <script>
+ function runTest(name, id, x1, y1, x2, y2)
+ {
+ debug("");
+ debug(name);
+ var node = document.getElementById(id);
+ p = webkitConvertPointFromNodeToPage(node, new WebKitPoint(0, 0));
+ x = Math.round(p.x);
+ y = Math.round(p.y);
+ shouldBe('x', x1+"");
+ shouldBe('y', y1+"");
+
+ debug("Round Trip of (0,0)");
+ p2 = webkitConvertPointFromPageToNode(node, p);
+ x = Math.round(p2.x);
+ y = Math.round(p2.y);
+ if (x == -0)
+ x = 0;
+ if (y == -0)
+ y = 0;
+ shouldBe('x', '0');
+ shouldBe('y', '0');
+
+ p = webkitConvertPointFromNodeToPage(node, new WebKitPoint(5, 40));
+ x = Math.round(p.x);
+ y = Math.round(p.y);
+ if (x == -0)
+ x = 0;
+ if (y == -0)
+ y = 0;
+ shouldBe('x', x2+"");
+ shouldBe('y', y2+"");
+
+ debug("Round Trip of (5,40)");
+ p2 = webkitConvertPointFromPageToNode(node, p);
+ x = Math.round(p2.x);
+ y = Math.round(p2.y);
+ if (x == -0)
+ x = 0;
+ if (y == -0)
+ y = 0;
+ shouldBe('x', '5');
+ shouldBe('y', '40');
+ }
+
+ function run() {
+ description("This test exercises the webkitConvertPointFromNodeToPage() function");
+
+ debug("Test parameter passing - should not crash");
+ var point = webkitConvertPointFromNodeToPage(new WebKitPoint(0, 0));
+ if (point == null)
+ testPassed("Missing parameter test");
+ else
+ testFailed("Missing parameter test");
+ debug("Test did not crash and therefore was successful");
+ debug("");
+ point = webkitConvertPointFromNodeToPage(null, new WebKitPoint(0, 0));
+ if (point == null)
+ testPassed("null parameter test a");
+ else
+ testFailed("null parameter test a");
+ debug("Test did not crash and therefore was successful");
+ debug("");
+ point = webkitConvertPointFromNodeToPage(null, null);
+ if (point == null)
+ testPassed("null parameter test b");
+ else
+ testFailed("null parameter test b");
+ debug("Test did not crash and therefore was successful");
+
+ runTest("Test 1", "test1", 8, 13, 13, 53);
+ runTest("Test 2", "test2", 8, 51, 13, 91);
+ runTest("Test 3", "test3", 8, 85, 13, 125);
+ runTest("Test 4", "test4", 8, 119, 13, 159);
+ runTest("Test 5", "test5", 28, 153, 33, 193);
+ runTest("Test 6", "test6", 28, 187, 33, 227);
+ runTest("Test 7", "test7", 8, 239, 13, 279);
+ runTest("Test 8", "test8", 8, 273, 13, 313);
+ runTest("Test 9", "test9", 28, 291, 33, 331);
+ runTest("Test 10", "test10", 28, 309, 33, 349);
+ runTest("Test 11", "test11", 158, 376, 174, 394);
+ runTest("Test 12", "test12", 168, 451, 184, 469);
+ runTest("Test 13", "test13", 28, 487, 33, 527);
+
+ isSuccessfullyParsed();
+ }
+
+ window.onload = run;
+
+ </script>
+ </head>
+ <body>
+ <h1 id="test1">Test 1 - This is a H1 heading.</h1>
+ <p id="test2">Test 2 - This is a simple paragraph.</p>
+ <p id="test3">Test 3 - This is a paragraph with a <b>nested</b> element.</p>
+ <p id="test4">Test 4 - This is a paragraph with a <b class="border">nested</b> element that has a border.</p>
+ <p id="test5" class="c">Test 5 - This is a transformed paragraph with a <b class="border">nested</b> element that has a border.</p>
+ <p id="test6" class="c">Test 6 - This is a transformed paragraph with a <b class="border">nested</b> element that has a border.<br>And then a second line.</p>
+ <div class="d">
+ <p id="test7">Test 7 - This is a paragraph inside something that does not have a compositing layer.</p>
+ </div>
+ <div id="test8" class="d">
+ Test 8 - This is raw text inside something that does not have a compositing layer.
+ </div>
+
+ <div id="test9" class="b">
+ Test 9 - This is raw text inside something that has a compositing layer.
+ <div id="test10" class="d">
+ Test 10 - This is raw text inside something that does not have a compositing layer.
+ </div>
+ </div>
+
+ <div class="a">
+ <p id="test11">Test 11 - This is a rotated and scaled paragraph</p>
+ </div>
+ <div class="a">
+ <div class="b">
+ <p id="test12">Test 12 - This is a rotated and scaled paragraph with a <b class="border">nested</b> element that has a border.</p>
+ </div>
+ </div>
+ <div class="b">
+ <p id="test13">Test 13 - This is a paragraph with a <b class="border">nested</b> element that has a border.</p>
+ </div>
+ <div id="description"></div>
+ <div id="console"></div>
+ </body>
+</html>
--- /dev/null
+This test exercises the webkitConvertPointFromNodeToPage() function after changing the transform.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+PASS x is 60
+PASS y is 70
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<html>
+ <head>
+ <title>webkitConvertPointFromNodeToPage Test</title>
+ <script src="../../../resources/js-test.js"></script>
+ <style type="text/css" media="screen">
+
+ #test {
+ position: absolute;
+ left: 10px;
+ top: 10px;
+ width: 50px;
+ height: 50px;
+ background-color: blue;
+ }
+
+ </style>
+
+ <script type="text/javascript" charset="utf-8">
+ function doTest()
+ {
+ description("This test exercises the webkitConvertPointFromNodeToPage() function after changing the transform.");
+
+ var node = document.getElementById('test');
+ node.style.webkitTransform = 'translate(50px, 60px)';
+ var p = webkitConvertPointFromNodeToPage(node, new WebKitPoint(0, 0));
+ x = Math.round(p.x);
+ y = Math.round(p.y);
+ shouldBe('x', 60 + "");
+ shouldBe('y', 70 + "");
+
+ isSuccessfullyParsed();
+ }
+
+ window.onload = doTest;
+ </script>
+
+ </head>
+ <body>
+ <div id="test"></div>
+
+ <div id="description" style="margin-top: 200px"></div>
+ <div id="console"></div>
+ </body>
+</html>
TouchStart: 1
TouchMove: 0
TouchEnd: 0
-GestureTapDown: 1
-GestureShowPress: 2
+GestureTapDown: 2
+GestureShowPress: 4
GestureTap: 7
GestureScrollBegin: 1
GestureTapCancel: 1
TouchStart: 1 1 1
TouchMove: 0 0 0
TouchEnd: 0 0 0
-GestureTapDown: 1 1 1
-GestureShowPress: 2 2 2
+GestureTapDown: 1 1 2
+GestureShowPress: 2 2 4
GestureTap: 3 3 7
GestureScrollBegin: 1 1 1
GestureTapCancel: 1 1 1
TouchEnd: 0 0 0
GestureTapDown: 1 4 16
GestureShowPress: 2 8 32
-GestureTap: 3 12 32
+GestureTap: 4 12 32
GestureScrollBegin: 1 1 0
GestureTapCancel: 1 1 0
GestureScrollUpdate: 0 0 0
--- /dev/null
+This tests scroll gesture events on main frame scroll bars. The document should be slightly scrolled down if successful.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS window.innerWidth - document.body.clientWidth is > 5
+PASS window.scrollY is 0
+PASS window.scrollY is 0
+PASS window.scrollY is > 20
+PASS window.scrollY is > 85
+PASS window.scrollY is > 85
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE html>
+<script src="../../../../resources/js-test.js"></script>
+<style>
+::-webkit-scrollbar {
+ background-color: #ccc;
+ /* fixed size for consistent touch adjustment behavior across platforms */
+ width: 15px;
+}
+
+::-webkit-scrollbar-button {
+ display: none;
+}
+
+::-webkit-scrollbar-thumb {
+ background-color: #777;
+ width: 15px;
+}
+
+body {
+ margin: 0;
+}
+
+.large {
+ height: 2000px;
+ width: 600px;
+}
+</style>
+<div id="console"></div>
+<div class="large">
+
+<script type="text/javascript">
+
+// Ensure there's a candidate for touch adjustment.
+document.addEventListener("click", function() {});
+
+function scrollTest() {
+ shouldBeGreaterThan("window.innerWidth - document.body.clientWidth", "5");
+ var scrollbarX = document.body.clientWidth + 5;
+ var scrollbarY = 50;
+
+ // Ensure we use a touch with an area to test under touch adjustment
+ var touchWidth = 25;
+ var touchHeight = 25;
+
+ shouldBe('window.scrollY', '0');
+ eventSender.gestureTapDown(scrollbarX, scrollbarY, touchWidth, touchHeight);
+ eventSender.gestureShowPress(scrollbarX, scrollbarY, touchWidth, touchHeight);
+ eventSender.gestureScrollBegin(scrollbarX - 20, scrollbarY, touchWidth, touchHeight);
+ eventSender.gestureTapCancel(scrollbarX, scrollbarY, touchWidth, touchHeight);
+ shouldBe('window.scrollY', '0');
+ eventSender.gestureScrollUpdate(0, 20);
+ shouldBeGreaterThan('window.scrollY', '20');
+ eventSender.gestureScrollUpdate(0, 60);
+ shouldBeGreaterThan('window.scrollY', '85');
+ eventSender.gestureScrollEnd(0, 0);
+ shouldBeGreaterThan('window.scrollY', '85');
+}
+
+//if (window.internals)
+// internals.settings.setMockScrollbarsEnabled(true);
+
+description('This tests scroll gesture events on main frame scroll bars. ' +
+ 'The document should be slightly scrolled down if successful.');
+
+if (window.eventSender) {
+ scrollTest();
+} else {
+ debug("This test requires eventSender");
+}
+</script>
function scrollTest() {
movingDiv = document.getElementById('scrollable');
+
+ // Ensure the div is a target for touch adjustment.
+ movingDiv.addEventListener('click', function() {});
+
var scrollbarX = movingDiv.offsetLeft + movingDiv.offsetWidth - 5;
var scrollThumbSafeOffset = 80;
var scrollbarY = movingDiv.offsetTop + scrollThumbSafeOffset;
--- /dev/null
+Verifies that the element receiving the :active style is the same as the element receiving the click event, even in the presence of difficult touch adjustment scenarios
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Sending gestureTapDown
+PASS getHoverActiveState(target) is "default"
+Sending gestureShowPress
+PASS getHoverActiveState(target) is "hoveredAndActive"
+Sending gestureTap
+PASS clickTarget.id is "target"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE html>
+<script src="../../../../resources/js-test.js"></script>
+<script src="../resources/touch-hover-active-tests.js"></script>
+<link rel="stylesheet" href="../resources/touch-hover-active-tests.css">
+<style>
+#target {
+ position: absolute;
+ height: 100px;
+ left: 50px;
+ top: 100px;
+ width: 20px;
+}
+
+#clip {
+ position: absolute;
+ top: 110px;
+ left: 0;
+ width: 50px;
+ height: 80px;
+ overflow: hidden;
+}
+
+#fakeTarget {
+ width: 150px;
+ height: 150px;
+ background-color: lightblue;
+}
+
+#console {
+ margin-top: 200px;
+}
+</style>
+
+<div id=target class=touch-interactive></div>
+<div id=clip>
+ <div id=fakeTarget class=touch-interactive></div>
+</div>
+
+<div id=console></div>
+
+</style>
+<script>
+description("Verifies that the element receiving the :active style is the same as the element receiving the click event, even in the presence of difficult touch adjustment scenarios");
+
+var clickTarget;
+document.addEventListener('click', function(e) {
+ if (clickTarget)
+ testFailed('Saw unexpected duplicate click event');
+ clickTarget = e.target;
+});
+
+var rect = target.getBoundingClientRect();
+var x = rect.left + rect.width / 2;
+var y = rect.top + rect.height / 2;
+
+debug('Sending gestureTapDown');
+eventSender.gestureTapDown(x, y, 30, 30);
+shouldBeDefault('getHoverActiveState(target)');
+
+debug('Sending gestureShowPress');
+eventSender.gestureShowPress(x, y, 30, 30);
+shouldBeHoveredAndActive('getHoverActiveState(target)');
+
+debug('Sending gestureTap');
+eventSender.gestureTap(x, y, 1, 30, 30);
+shouldBeEqualToString("clickTarget.id", "target");
+
+</script>
--- /dev/null
+Verify that an aborted and stopping FileReader doesn't crash
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS No crash
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE html>
+<script src="../../resources/js-test.js"></script>
+<script>
+description("Verify that an aborted and stopping FileReader doesn't crash");
+
+window.jsTestIsAsync = true;
+
+if (window.testRunner)
+ testRunner.dumpAsText();
+
+var reader;
+function setReader(r) {
+ reader = r;
+}
+
+function runTest() {
+ reader.readAsText(new Blob());
+ reader.abort();
+ document.body.removeChild(document.getElementById('ifr'));
+ reader = null;
+ gc();
+ testPassed("No crash");
+ finishJSTest();
+}
+</script>
+<iframe id=ifr src="resources/file-reader-abort-gc-iframe.html"></iframe>
--- /dev/null
+<script>
+window.parent.setReader(new FileReader());
+window.parent.runTest();
+</script>
--- /dev/null
+PASS; no crash.
--- /dev/null
+<!DOCTYPE html>
+<body>
+<div>
+<div><form></div>
+<button id=b></button>
+</div>
+</body>
+<script>
+if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.waitUntilDone();
+}
+var b = document.getElementById('b');
+document.body.innerHTML = '';
+setTimeout(function() {
+ if (window.GCController)
+ GCController.collectAll();
+ document.body.appendChild(b);
+ document.body.innerHTML = 'PASS; no crash.';
+ if (window.testRunner)
+ testRunner.notifyDone();
+}, 0);
+</script>
--- /dev/null
+<input id="input" value="hello" style="width: 99px" >
+<script>
+input.select();
+internals.setAutofilled(input, true);
+</script>
\ No newline at end of file
--- /dev/null
+<input id="input" >
+<script>
+input.focus();
+internals.setSuggestedValue(input, '');
+internals.setSuggestedValue(input, 'hello');
+internals.setAutofilled(input, true);
+input.style.width = '99px';
+input.setSelectionRange(0, 5);
+</script>
--- /dev/null
+Tapping on listbox items should fire an input and a change event.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS changeEventCounter is 0
+PASS inputEventCounter is 0
+PASS changeEventCounter is 1
+PASS inputEventCounter is 1
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../../resources/js-test.js"></script>
+<select id="select1" multiple size="4">
+<option>1</option>
+<option>2</option>
+<option>3</option>
+</select>
+
+<script>
+description('Tapping on listbox items should fire an input and a change event.');
+
+function tapOption(select, index) {
+ var itemHeight = Math.floor(select.offsetHeight / select.size);
+ var border = 1;
+ var y = border + index * itemHeight;
+
+ select.focus();
+ if (window.eventSender) {
+ eventSender.gestureTap(select.offsetLeft + border, select.offsetTop + y - window.pageYOffset + itemHeight / 2);
+ }
+}
+
+var select = document.getElementById('select1');
+var changeEventCounter = 0;
+var inputEventCounter = 0;
+
+select.addEventListener('change', function () {
+ changeEventCounter++;
+}, false);
+
+select.addEventListener('input', function () {
+ inputEventCounter++;
+}, false);
+
+if (window.eventSender) {
+ shouldBe('changeEventCounter', '0');
+ shouldBe('inputEventCounter', '0');
+ tapOption(select, 0);
+ shouldBe('changeEventCounter', '1');
+ shouldBe('inputEventCounter', '1');
+}
+</script>
+
+</html>
src: url('../../../resources/opensans/OpenSans-Regular.woff') format('woff');
}
section > div > div {
- font-family: 'Open Sans';
+ font-family: 'Open Sans', 'Segoe UI';
}
</style>
<script src="resources/text-scaling.js"></script>
var id2 = "b";
var element1 = document.getElementById(id1);
var element2 = document.getElementById(id2);
- var x1 = element1.getBoundingClientRect().left;
- var y1 = element1.getBoundingClientRect().top;
- var x2 = element2.getBoundingClientRect().left;
- var y2 = element2.getBoundingClientRect().top;
+ var x1 = webkitConvertPointFromNodeToPage(element1, new WebKitPoint(0,0)).x;
+ var y1 = webkitConvertPointFromNodeToPage(element1, new WebKitPoint(0,0)).y;
+ var x2 = webkitConvertPointFromNodeToPage(element2, new WebKitPoint(0,0)).x;
+ var y2 = webkitConvertPointFromNodeToPage(element2, new WebKitPoint(0,0)).y;
var resultString = '';
if (x1 == x2 && y1 == y2) {
--- /dev/null
+This is a regression test for crbug.com/400476. It should not crash and then brag about it.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+[object NodeList]
+PASS totally did not crash.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<html>
+<head>
+<script src="/js-test-resources/js-test.js"></script>
+</head>
+<body>
+<script>
+var jsTestIsAsync = true;
+description('This is a regression test for crbug.com/400476. It should not crash and then brag about it.')
+
+var root = document.documentElement;
+var iframe = root.ownerDocument.createElement('iframe');
+var timeouts = [];
+iframe.onload = iframeOnload;
+root.appendChild(iframe);
+
+function iframeOnload() {
+ var defaultView = iframe.contentDocument.defaultView;
+ defaultView.onpageshow = onPageShow;
+ iframe.src = null;
+ timeouts[timeouts.length] = window.setTimeout(nextIframeLoaded, 100);
+}
+
+function onPageShow() {
+ eventObj = arguments[0];
+}
+
+function nextIframeLoaded() {
+ timeouts.forEach(window.clearTimeout);
+
+ // Access of eventObj.path caused the crash.
+ // The test is somewhat flaky, in that the test may pass as correct
+ // despite the bug being the code. The exact conditions
+ // are unclear, but 1, asan helps detect the crash and 2, the
+ // preceeding gc()s increase the likelihood of it occurring.
+ gc();
+ gc();
+ gc();
+ gc();
+ gc();
+ var path = eventObj.path;
+ debug(path);
+
+ testPassed('totally did not crash.');
+ finishJSTest();
+}
+</script>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<style>
+test {
+ content: url("http://127.0.0.1:8080/security/resources/green250x50.png");
+}
+</style>
+<body>
+<p>
+Checks that an CSS image that was created before a referrer policy was set is
+loaded with the correct referrer, in this case, without a referrer.
+</p>
+<p>
+The test passes, if a green rectangle is displayed below.
+</p>
+<test/>
+</body>
--- /dev/null
+<!DOCTYPE html>
+<script>
+if (window.testRunner)
+ testRunner.waitUntilDone();
+document.location = "https://127.0.0.1:8443/security/resources/referrer-policy-conflicting-policies.html";
+</script>
--- /dev/null
+<?php
+header('Content-Type: image/png');
+if ($_SERVER['HTTP_REFERER'] != '') {
+ $img = 'red200x100.png';
+} else {
+ $img = 'green250x50.png';
+}
+echo file_get_contents($img);
+?>
--- /dev/null
+<!DOCTYPE html>
+<script>
+if (window.testRunner)
+ testRunner.setAllowDisplayOfInsecureContent();
+
+function done()
+{
+ if (window.testRunner)
+ testRunner.notifyDone();
+}
+</script>
+<style>
+test {
+ content: url("http://127.0.0.1:8080/security/resources/green-if-no-referrer.php");
+}
+</style>
+<meta name="referrer" content="origin" />
+<body onload="done();">
+<p>
+Checks that an CSS image that was created before a referrer policy was set is
+loaded with the correct referrer, in this case, without a referrer.
+</p>
+<p>
+The test passes, if a green rectangle is displayed below.
+</p>
+<test/>
+</body>
var audio = document.getElementById("audio");
audio.addEventListener("playing", function()
{
- runAfterControlsHidden(function()
+ runAfterHideMediaControlsTimerFired(function()
{
controls = mediaControlsButton(audio, "panel");
testExpected("getComputedStyle(controls).opacity", 1);
eventSender.mouseUp();
}
-function runAfterControlsHidden(func, mediaElement)
+function runAfterHideMediaControlsTimerFired(func, mediaElement)
{
if (mediaElement.paused)
throw "The media element is not playing";
--- /dev/null
+Test video element control visibility after click on control. After the click the mouse does not move, so the control is still hovered and it should remain visible.
+
+This test only runs in DRT!
+
+EXPECTED (video.paused == 'true') OK
+EXPECTED (video.paused == 'false') OK
+EXPECTED (getComputedStyle(controls).opacity == '1') OK
+
+END OF TEST
+
--- /dev/null
+<!DOCTYPE html>
+<html>
+<style>
+#no-video-media {
+ width: 320px;
+ height: 240px;
+}
+</style>
+<script src=video-test.js></script>
+<script src=media-file.js></script>
+<script src=media-controls.js></script>
+<script>
+var controls;
+
+function runTest()
+{
+ video = document.getElementById("no-video-media");
+
+ testExpected("video.paused", true);
+ if (!window.testRunner)
+ return;
+
+ // Click the play button.
+ var playCoords = mediaControlsButtonCoordinates(video, "play-button");
+ var clickX = playCoords[0];
+ var clickY = playCoords[1];
+ eventSender.mouseMoveTo(clickX, clickY);
+ eventSender.mouseDown();
+ eventSender.mouseUp();
+ testExpected("video.paused", false);
+
+ runAfterHideMediaControlsTimerFired(function()
+ {
+ controls = mediaControlsButton(video, "panel");
+ testExpected("getComputedStyle(controls).opacity", 1);
+
+ consoleWrite("");
+ endTest();
+ }, video);
+}
+</script>
+<body>
+ <p>Test video element control visibility after click on control. After the click
+ the mouse does not move, so the control is still hovered and it should
+ remain visible.</p>
+ <p>This test only runs in DRT!</p>
+
+ <video id="no-video-media" controls loop oncanplaythrough="runTest()"></video>
+ <script>
+ setSrcById("no-video-media", findMediaFile("video", "content/test"));
+ </script>
+</body>
+</html>
testExpected("document.activeElement", video);
- runAfterControlsHidden(function()
+ runAfterHideMediaControlsTimerFired(function()
{
controls = mediaControlsButton(video, "panel");
testExpected("getComputedStyle(controls).opacity", 0);
--- /dev/null
+Test video element control visibility after touch on control. After a delay the controls must be hidden.
+
+This test only runs in DRT!
+
+EXPECTED (video.paused == 'true') OK
+EXPECTED (video.paused == 'false') OK
+EXPECTED (getComputedStyle(controls).opacity == '0') OK
+
+END OF TEST
+
--- /dev/null
+<!DOCTYPE html>
+<html>
+<style>
+#no-video-media {
+ width: 320px;
+ height: 240px;
+}
+</style>
+<script src=video-test.js></script>
+<script src=media-file.js></script>
+<script src=media-controls.js></script>
+<script>
+var controls;
+
+function runTest()
+{
+ video = document.getElementById("no-video-media");
+
+ testExpected("video.paused", true);
+ if (!window.testRunner)
+ return;
+
+ // Click the play button.
+ var playCoords = mediaControlsButtonCoordinates(video, "play-button");
+ var clickX = playCoords[0];
+ var clickY = playCoords[1];
+ eventSender.mouseMoveTo(clickX, clickY);
+ eventSender.mouseDown();
+ eventSender.mouseUp();
+ testExpected("video.paused", false);
+
+ runAfterHideMediaControlsTimerFired(function()
+ {
+ controls = mediaControlsButton(video, "panel");
+ testExpected("getComputedStyle(controls).opacity", 0);
+
+ consoleWrite("");
+ endTest();
+ }, video);
+}
+</script>
+<body>
+ <p>Test video element control visibility after touch on control. After a delay the
+ controls must be hidden.</p>
+ <p>This test only runs in DRT!</p>
+
+ <video id="no-video-media" controls loop oncanplaythrough="runTest()"></video>
+ <script>
+ window.internals.settings.setDeviceSupportsMouse(false);
+ setSrcById("no-video-media", findMediaFile("video", "content/test"));
+ </script>
+</body>
+</html>
eventSender.mouseUp();
testExpected("video.paused", false);
- runAfterControlsHidden(function()
+ runAfterHideMediaControlsTimerFired(function()
{
controls = mediaControlsButton(video, "panel");
testExpected("getComputedStyle(controls).opacity", 0);
video.addEventListener("playing", function()
{
- runAfterControlsHidden(function()
+ runAfterHideMediaControlsTimerFired(function()
{
controls = mediaControlsButton(video, "panel");
{
switch (count) {
case 0:
- runAfterControlsHidden(continueTest, video);
+ runAfterHideMediaControlsTimerFired(continueTest, video);
break;
case 1:
consoleWrite("");
--- /dev/null
+Test 1 - This is a H1 heading.
+
+Test 2 - This is a simple paragraph.
+
+Test 3 - This is a paragraph with a nested element.
+
+Test 4 - This is a paragraph with a nested element that has a border.
+
+Test 5 - This is a transformed paragraph with a nested element that has a border.
+
+Test 6 - This is a transformed paragraph with a nested element that has a border.
+And then a second line.
+
+Test 7 - This is a paragraph inside something that does not have a compositing layer.
+
+Test 8 - This is raw text inside something that does not have a compositing layer.
+Test 9 - This is raw text inside something that has a compositing layer.
+Test 10 - This is raw text inside something that does not have a compositing layer.
+Test 11 - This is a rotated and scaled paragraph
+
+Test 12 - This is a rotated and scaled paragraph with a nested element that has a border.
+
+Test 13 - This is a paragraph with a nested element that has a border.
+
+This test exercises the webkitConvertPointFromNodeToPage() function
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Test parameter passing - should not crash
+PASS Missing parameter test
+Test did not crash and therefore was successful
+
+PASS null parameter test a
+Test did not crash and therefore was successful
+
+PASS null parameter test b
+Test did not crash and therefore was successful
+
+Test 1
+PASS x is 8
+PASS y is 13
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+PASS y is 53
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 2
+PASS x is 8
+FAIL y should be 51. Was 52.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+FAIL y should be 91. Was 92.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 3
+PASS x is 8
+FAIL y should be 85. Was 88.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+FAIL y should be 125. Was 128.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 4
+PASS x is 8
+FAIL y should be 119. Was 124.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+FAIL y should be 159. Was 164.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 5
+PASS x is 28
+FAIL y should be 153. Was 160.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+FAIL y should be 193. Was 200.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 6
+PASS x is 28
+FAIL y should be 187. Was 196.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+FAIL y should be 227. Was 236.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 7
+PASS x is 8
+FAIL y should be 239. Was 252.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+FAIL y should be 279. Was 292.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 8
+PASS x is 8
+FAIL y should be 273. Was 288.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+FAIL y should be 313. Was 328.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 9
+PASS x is 28
+FAIL y should be 291. Was 308.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+FAIL y should be 331. Was 348.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 10
+PASS x is 28
+FAIL y should be 309. Was 328.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+FAIL y should be 349. Was 368.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 11
+PASS x is 158
+PASS y is 376
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 174
+PASS y is 394
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 12
+PASS x is 168
+PASS y is 451
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 184
+PASS y is 469
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 13
+PASS x is 28
+FAIL y should be 487. Was 512.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+FAIL y should be 527. Was 552.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+Test 1 - This is a H1 heading.
+
+Test 2 - This is a simple paragraph.
+
+Test 3 - This is a paragraph with a nested element.
+
+Test 4 - This is a paragraph with a nested element that has a border.
+
+Test 5 - This is a transformed paragraph with a nested element that has a border.
+
+Test 6 - This is a transformed paragraph with a nested element that has a border.
+And then a second line.
+
+Test 7 - This is a paragraph inside something that does not have a compositing layer.
+
+Test 8 - This is raw text inside something that does not have a compositing layer.
+Test 9 - This is raw text inside something that has a compositing layer.
+Test 10 - This is raw text inside something that does not have a compositing layer.
+Test 11 - This is a rotated and scaled paragraph
+
+Test 12 - This is a rotated and scaled paragraph with a nested element that has a border.
+
+Test 13 - This is a paragraph with a nested element that has a border.
+
+This test exercises the webkitConvertPointFromNodeToPage() function
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Test parameter passing - should not crash
+PASS Missing parameter test
+Test did not crash and therefore was successful
+
+PASS null parameter test a
+Test did not crash and therefore was successful
+
+PASS null parameter test b
+Test did not crash and therefore was successful
+
+Test 1
+PASS x is 8
+PASS y is 13
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+PASS y is 53
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 2
+PASS x is 8
+FAIL y should be 51. Was 52.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+FAIL y should be 91. Was 92.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 3
+PASS x is 8
+FAIL y should be 85. Was 88.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+FAIL y should be 125. Was 128.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 4
+PASS x is 8
+FAIL y should be 119. Was 124.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+FAIL y should be 159. Was 164.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 5
+PASS x is 28
+FAIL y should be 153. Was 160.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+FAIL y should be 193. Was 200.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 6
+PASS x is 28
+FAIL y should be 187. Was 196.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+FAIL y should be 227. Was 236.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 7
+PASS x is 8
+FAIL y should be 239. Was 252.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+FAIL y should be 279. Was 292.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 8
+PASS x is 8
+FAIL y should be 273. Was 288.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 13
+FAIL y should be 313. Was 328.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 9
+PASS x is 28
+FAIL y should be 291. Was 308.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+FAIL y should be 331. Was 348.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 10
+PASS x is 28
+FAIL y should be 309. Was 328.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+FAIL y should be 349. Was 368.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 11
+PASS x is 158
+PASS y is 376
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 174
+PASS y is 394
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 12
+PASS x is 168
+PASS y is 451
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 184
+PASS y is 469
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+
+Test 13
+PASS x is 28
+FAIL y should be 487. Was 512.
+Round Trip of (0,0)
+PASS x is 0
+PASS y is 0
+PASS x is 33
+FAIL y should be 527. Was 552.
+Round Trip of (5,40)
+PASS x is 5
+PASS y is 40
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+This test checks that page scale does not affect a plugin object's height when it depends on the window height. The test scales the page and is considered to pass if the embed object occupies the full height of the window.
+
+EXPECTED:
+layer at (0,0) size 800x600 RenderEmbeddedObject {EMBED} at (0,0) size 800x600
+ACTUAL:
+layer at (0,0) size 800x600 RenderEmbeddedObject {EMBED} at (0,0) size 800x600
--- /dev/null
+<!DOCTYPE html>
+<script>
+ function runTest() {
+ if (window.testRunner)
+ testRunner.dumpAsText();
+
+ if (window.eventSender)
+ window.eventSender.setPageScaleFactor(1.5, 0, 0);
+
+ document.body.innerHTML =
+ "This test checks that page scale does not affect a plugin object's height when it depends on the window height."
+ + " The test scales the page and is considered to pass if the embed object occupies the full height of the window."
+ + "<br>"
+ + "<br>"
+ + "EXPECTED:<br>"
+ + "layer at (0,0) size 800x600 RenderEmbeddedObject {EMBED} at (0,0) size 800x600<br>"
+ + "ACTUAL:<br>"
+ + internals.elementRenderTreeAsText(document.getElementById('box'));
+ }
+</script>
+<style>
+html, body {
+ height: 100%;
+}
+</style>
+<body style="margin: 0px; overflow: hidden" onload="runTest()">
+ <embed id="box" width="100%" height="100%" name="plugin" src="fileDoesNotExist.pdf" type="application/pdf"></embed>
+</body>
var id2 = "b";
var element1 = document.getElementById(id1);
var element2 = document.getElementById(id2);
- var x1 = element1.getBoundingClientRect().left;
- var y1 = element1.getBoundingClientRect().top;
- var x2 = element2.getBoundingClientRect().left;
- var y2 = element2.getBoundingClientRect().top;
+ var x1 = webkitConvertPointFromNodeToPage(element1, new WebKitPoint(0,0)).x;
+ var y1 = webkitConvertPointFromNodeToPage(element1, new WebKitPoint(0,0)).y;
+ var x2 = webkitConvertPointFromNodeToPage(element2, new WebKitPoint(0,0)).x;
+ var y2 = webkitConvertPointFromNodeToPage(element2, new WebKitPoint(0,0)).y;
var resultString = '';
if (x1 == x2 && y1 == y2) {
function checkPosition(id) {
var element = document.getElementById(id);
- var y = element.getBoundingClientRect().top;
+ var y = webkitConvertPointFromNodeToPage(element, new WebKitPoint(0,0)).y;
var resultString = '';
if (y > 250) {
var id2 = "b";
var element1 = document.getElementById(id1);
var element2 = document.getElementById(id2);
- var x1 = element1.getBoundingClientRect().left;
- var y1 = element1.getBoundingClientRect().top;
- var x2 = element2.getBoundingClientRect().left;
- var y2 = element2.getBoundingClientRect().top;
+ var x1 = webkitConvertPointFromNodeToPage(element1, new WebKitPoint(0,0)).x;
+ var y1 = webkitConvertPointFromNodeToPage(element1, new WebKitPoint(0,0)).y;
+ var x2 = webkitConvertPointFromNodeToPage(element2, new WebKitPoint(0,0)).x;
+ var y2 = webkitConvertPointFromNodeToPage(element2, new WebKitPoint(0,0)).y;
var resultString = '';
if (x1 == x2 && y1 == y2) {
var id2 = "b";
var element1 = document.getElementById(id1);
var element2 = document.getElementById(id2);
- var x1 = element1.getBoundingClientRect().left;
- var y1 = element1.getBoundingClientRect().top;
- var x2 = element2.getBoundingClientRect().left;
- var y2 = element2.getBoundingClientRect().top;
+ var x1 = webkitConvertPointFromNodeToPage(element1, new WebKitPoint(0,0)).x;
+ var y1 = webkitConvertPointFromNodeToPage(element1, new WebKitPoint(0,0)).y;
+ var x2 = webkitConvertPointFromNodeToPage(element2, new WebKitPoint(0,0)).x;
+ var y2 = webkitConvertPointFromNodeToPage(element2, new WebKitPoint(0,0)).y;
var resultString = '';
if (x1 == x2 && y1 == y2) {
DOCUMENT_POSITION_PRECEDING | DOCUMENT_POSITION_CONTAINS | connection;
}
+FloatPoint Node::convertToPage(const FloatPoint& p) const
+{
+ // If there is a renderer, just ask it to do the conversion
+ if (renderer())
+ return renderer()->localToAbsolute(p, UseTransforms);
+
+ // Otherwise go up the tree looking for a renderer
+ if (Element* parent = parentElement())
+ return parent->convertToPage(p);
+
+ // No parent - no conversion needed
+ return p;
+}
+
+FloatPoint Node::convertFromPage(const FloatPoint& p) const
+{
+ // If there is a renderer, just ask it to do the conversion
+ if (renderer())
+ return renderer()->absoluteToLocal(p, UseTransforms);
+
+ // Otherwise go up the tree looking for a renderer
+ if (Element* parent = parentElement())
+ return parent->convertFromPage(p);
+
+ // No parent - no conversion needed
+ return p;
+}
+
String Node::debugName() const
{
StringBuilder name;
// Whether or not a selection can be started in this object
virtual bool canStartSelection() const;
+ // Getting points into and out of screen space
+ FloatPoint convertToPage(const FloatPoint&) const;
+ FloatPoint convertFromPage(const FloatPoint&) const;
+
// -----------------------------------------------------------------------------
// Integration with rendering tree
#include "config.h"
#include "core/dom/TreeScopeAdopter.h"
+#include "core/accessibility/AXObjectCache.h"
#include "core/dom/Attr.h"
#include "core/dom/NodeRareData.h"
#include "core/dom/NodeTraversal.h"
Document& oldDocument = oldScope().document();
Document& newDocument = newScope().document();
bool willMoveToNewDocument = oldDocument != newDocument;
+ AXObjectCache* axObjectCache = oldDocument.existingAXObjectCache();
if (willMoveToNewDocument)
oldDocument.incDOMTreeVersion();
for (Node* node = &root; node; node = NodeTraversal::next(*node, &root)) {
updateTreeScope(*node);
- if (willMoveToNewDocument)
+ if (willMoveToNewDocument) {
+ if (axObjectCache)
+ axObjectCache->remove(node);
moveNodeToNewDocument(*node, oldDocument, newDocument);
- else if (node->hasRareData()) {
+ } else if (node->hasRareData()) {
NodeRareData* rareData = node->rareData();
if (rareData->nodeLists())
rareData->nodeLists()->adoptTreeScope();
return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
}
-void CaretBase::repaintCaretForLocalRect(Node* node, const LayoutRect& rect)
+void CaretBase::invalidateLocalCaretRect(Node* node, const LayoutRect& rect)
{
RenderBlock* caretPainter = caretRenderer(node);
if (!caretPainter)
if (RenderView* view = node->document().renderView()) {
if (shouldRepaintCaret(view, node->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable)))
- repaintCaretForLocalRect(node, localCaretRectWithoutUpdate());
+ invalidateLocalCaretRect(node, localCaretRectWithoutUpdate());
}
}
protected:
static RenderBlock* caretRenderer(Node*);
- static void repaintCaretForLocalRect(Node*, const LayoutRect&);
+ static void invalidateLocalCaretRect(Node*, const LayoutRect&);
private:
LayoutRect m_caretLocalRect; // caret rect in coords local to the renderer responsible for painting the caret
void DOMSelection::collapse(Node* node, int offset, ExceptionState& exceptionState)
{
- ASSERT(node);
if (!m_frame)
return;
+ if (!node) {
+ m_frame->selection().clear();
+ return;
+ }
+
if (offset < 0) {
exceptionState.throwDOMException(IndexSizeError, String::number(offset) + " is not a valid offset.");
return;
return localCaretRectOfPosition(caretPosition, renderer);
}
-static void invalidateLocalCaretRect(RenderObject* renderer, const LayoutRect& caretRect)
-{
- // FIXME: Need to over-paint 1 pixel to workaround some rounding problems.
- // https://bugs.webkit.org/show_bug.cgi?id=108283
- LayoutRect inflatedRect = caretRect;
- inflatedRect.inflate(1);
- renderer->invalidatePaintRectangle(inflatedRect);
-}
-
void FrameSelection::invalidateCaretRect()
{
if (!m_caretRectDirty)
RenderView* view = m_frame->document()->renderView();
if (m_previousCaretNode && shouldRepaintCaret(view, m_previousCaretNode->isContentEditable()))
- invalidateLocalCaretRect(m_previousCaretNode->renderer(), m_previousCaretRect);
+ invalidateLocalCaretRect(m_previousCaretNode.get(), m_previousCaretRect);
if (newNode && shouldRepaintCaret(view, newNode->isContentEditable()))
- invalidateLocalCaretRect(newNode->renderer(), newRect);
+ invalidateLocalCaretRect(newNode, newRect);
m_previousCaretNode = newNode;
m_previousCaretRect = newRect;
return;
}
+ m_frame->document()->updateLayoutIgnorePendingStylesheets();
+
// Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
// Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
// as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
#include "core/dom/Document.h"
#include "core/dom/Element.h"
#include "core/dom/Text.h"
+#include "core/frame/FrameView.h"
#include "core/html/HTMLBodyElement.h"
#include "core/html/HTMLDocument.h"
#include "core/testing/DummyPageHolder.h"
HTMLDocument& document() const;
void setSelection(const VisibleSelection&);
- const FrameSelection& selection() const;
+ FrameSelection& selection() const;
Text* textNode() { return m_textNode.get(); }
private:
m_dummyPageHolder->frame().selection().setSelection(newSelection);
}
-const FrameSelection& FrameSelectionTest::selection() const
+FrameSelection& FrameSelectionTest::selection() const
{
return m_dummyPageHolder->frame().selection();
}
EXPECT_TRUE(selection().isNone());
}
+TEST_F(FrameSelectionTest, InvalidateCaretRect)
+{
+ document().view()->updateLayoutAndStyleIfNeededRecursive();
+
+ VisibleSelection validSelection(Position(textNode(), 0), Position(textNode(), 0));
+ setSelection(validSelection);
+ selection().setCaretRectNeedsUpdate();
+ EXPECT_TRUE(selection().isCaretBoundsDirty());
+ selection().invalidateCaretRect();
+ EXPECT_FALSE(selection().isCaretBoundsDirty());
+
+ document().body()->removeChild(textNode());
+ document().updateLayoutIgnorePendingStylesheets();
+ selection().setCaretRectNeedsUpdate();
+ EXPECT_TRUE(selection().isCaretBoundsDirty());
+ selection().invalidateCaretRect();
+ EXPECT_FALSE(selection().isCaretBoundsDirty());
+}
+
}
m_compositionNode = toText(baseNode);
RefPtrWillBeRawPtr<Range> range = PlainTextRange(compositionStart, compositionEnd).createRange(*editable);
+ if (!range)
+ return;
+
m_compositionStart = range->startOffset();
m_compositionEnd = range->endOffset();
m_customCompositionUnderlines = underlines;
EXPECT_EQ(5u, plainTextRange.end());
}
+TEST_F(InputMethodControllerTest, SetCompositionFromExistingTextWithInvalidOffsets)
+{
+ insertHTMLElement("<div id='sample' contenteditable='true'>test</div>", "sample");
+
+ Vector<CompositionUnderline> underlines;
+ underlines.append(CompositionUnderline(7, 8, Color(255, 0, 0), false, 0));
+ controller().setCompositionFromExistingText(underlines, 7, 8);
+
+ EXPECT_FALSE(controller().compositionRange());
+}
+
} // namespace
readonly attribute long focusOffset;
readonly attribute boolean isCollapsed;
- [RaisesException, TypeChecking=Interface] void collapse(Node node, optional long offset);
+ [RaisesException] void collapse(Node? node, optional long offset);
[RaisesException] void collapseToStart();
[RaisesException] void collapseToEnd();
[Default=Undefined] optional long baseOffset,
[Default=Undefined] optional Node extentNode,
[Default=Undefined] optional long extentOffset);
- [ImplementedAs=collapse, MeasureAs=SelectionSetPosition, RaisesException, TypeChecking=Interface] void setPosition(Node node,
- optional long offset);
+ [ImplementedAs=collapse, MeasureAs=SelectionSetPosition, RaisesException] void setPosition(Node? node, optional long offset);
// IE extensions
// http://msdn.microsoft.com/en-us/library/ms535869(VS.85).aspx
if (SVGElement* svgElement = toSVGElement(node)->correspondingElement())
return svgElement;
}
- return m_currentTarget;
+ return m_currentTarget.get();
}
void Event::trace(Visitor* visitor)
bool m_cancelBubble;
unsigned short m_eventPhase;
- RawPtrWillBeMember<EventTarget> m_currentTarget;
+ RefPtrWillBeMember<EventTarget> m_currentTarget;
RefPtrWillBeMember<EventTarget> m_target;
DOMTimeStamp m_createTime;
RefPtrWillBeMember<Event> m_underlyingEvent;
ScriptWrappable::init(this);
}
-File::File(const String& name, const FileMetadata& metadata)
+File::File(const String& name, const FileMetadata& metadata, UserVisibility userVisibility)
: Blob(BlobDataHandle::create(createBlobDataForFileWithMetadata(name, metadata), metadata.length))
, m_hasBackingFile(true)
- , m_userVisibility(File::IsNotUserVisible)
+ , m_userVisibility(userVisibility)
, m_path(metadata.platformPath)
, m_name(name)
, m_snapshotSize(metadata.length)
// If filesystem files live in the remote filesystem, the port might pass the valid metadata (whose length field is non-negative) and cache in the File object.
//
// Otherwise calling size(), lastModifiedTime() and slice() will synchronously query the file metadata.
- static PassRefPtrWillBeRawPtr<File> createForFileSystemFile(const String& name, const FileMetadata& metadata)
+ static PassRefPtrWillBeRawPtr<File> createForFileSystemFile(const String& name, const FileMetadata& metadata, UserVisibility userVisibility)
{
- return adoptRefWillBeNoop(new File(name, metadata));
+ return adoptRefWillBeNoop(new File(name, metadata, userVisibility));
}
static PassRefPtrWillBeRawPtr<File> createForFileSystemFile(const KURL& url, const FileMetadata& metadata)
File(const String& path, const String& name, ContentTypeLookupPolicy, UserVisibility);
File(const String& path, const String& name, const String& relativePath, UserVisibility, bool hasSnaphotData, uint64_t size, double lastModified, PassRefPtr<BlobDataHandle>);
File(const String& name, double modificationTime, PassRefPtr<BlobDataHandle>);
- File(const String& name, const FileMetadata&);
+ File(const String& name, const FileMetadata&, UserVisibility);
File(const KURL& fileSystemURL, const FileMetadata&);
void invalidateSnapshotMetadata() { m_snapshotSize = -1; }
void FileReader::stop()
{
+ // The delayed abort task tidies up and advances to the DONE state.
+ if (m_loadingState == LoadingStateAborted)
+ return;
+
if (hasPendingActivity())
ThrottlingController::finishReader(executionContext(), this, ThrottlingController::removeReader(executionContext(), this));
terminate();
, m_inputEventsScaleFactorForEmulation(1)
, m_layoutSizeFixedToFrameSize(true)
, m_didScrollTimer(this, &FrameView::didScrollTimerFired)
+ , m_needsUpdateWidgetPositions(false)
{
ASSERT(m_frame);
init();
ScrollView::paintOverhangAreas(context, horizontalOverhangArea, verticalOverhangArea, dirtyRect);
}
+void FrameView::updateWidgetPositionsIfNeeded()
+{
+ if (!m_needsUpdateWidgetPositions)
+ return;
+
+ m_needsUpdateWidgetPositions = false;
+
+ updateWidgetPositions();
+}
+
void FrameView::updateLayoutAndStyleForPainting()
{
// Updating layout can run script, which can tear down the FrameView.
updateLayoutAndStyleIfNeededRecursive();
+ updateWidgetPositionsIfNeeded();
+
if (RenderView* view = renderView()) {
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateLayerTree", "frame", m_frame.get());
// FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
bool needsLayout() const;
void setNeedsLayout();
+ void setNeedsUpdateWidgetPositions() { m_needsUpdateWidgetPositions = true; }
+
// Methods for getting/setting the size Blink should use to layout the contents.
IntSize layoutSize(IncludeScrollbarsInRect = ExcludeScrollbars) const;
void setLayoutSize(const IntSize&);
virtual IntPoint convertToContainingView(const IntPoint&) const OVERRIDE;
virtual IntPoint convertFromContainingView(const IntPoint&) const OVERRIDE;
+ void updateWidgetPositionsIfNeeded();
+
void sendResizeEventIfNeeded();
void updateScrollableAreaSet();
Timer<FrameView> m_didScrollTimer;
Vector<IntRect> m_tickmarks;
+
+ bool m_needsUpdateWidgetPositions;
};
inline void FrameView::incrementVisuallyNonEmptyCharacterCount(unsigned count)
#include "core/frame/Navigator.h"
#include "core/frame/Screen.h"
#include "core/frame/Settings.h"
+#include "core/frame/WebKitPoint.h"
#include "core/html/HTMLFrameOwnerElement.h"
#include "core/inspector/ConsoleMessage.h"
#include "core/inspector/InspectorInstrumentation.h"
return m_frame->document()->ensureStyleResolver().pseudoCSSRulesForElement(element, pseudoId, rulesToInclude);
}
+PassRefPtrWillBeRawPtr<WebKitPoint> LocalDOMWindow::webkitConvertPointFromNodeToPage(Node* node, const WebKitPoint* p) const
+{
+ if (!node || !p)
+ return nullptr;
+
+ if (!document())
+ return nullptr;
+
+ document()->updateLayoutIgnorePendingStylesheets();
+
+ FloatPoint pagePoint(p->x(), p->y());
+ pagePoint = node->convertToPage(pagePoint);
+ return WebKitPoint::create(pagePoint.x(), pagePoint.y());
+}
+
+PassRefPtrWillBeRawPtr<WebKitPoint> LocalDOMWindow::webkitConvertPointFromPageToNode(Node* node, const WebKitPoint* p) const
+{
+ if (!node || !p)
+ return nullptr;
+
+ if (!document())
+ return nullptr;
+
+ document()->updateLayoutIgnorePendingStylesheets();
+
+ FloatPoint nodePoint(p->x(), p->y());
+ nodePoint = node->convertFromPage(nodePoint);
+ return WebKitPoint::create(nodePoint.x(), nodePoint.y());
+}
+
double LocalDOMWindow::devicePixelRatio() const
{
if (!m_frame)
class CSSRuleList;
class CSSStyleDeclaration;
class Console;
+ class WebKitPoint;
class DOMSelection;
class DOMURL;
class DOMWindowProperty;
PassRefPtrWillBeRawPtr<CSSRuleList> getMatchedCSSRules(Element*, const String& pseudoElt) const;
double devicePixelRatio() const;
+ PassRefPtrWillBeRawPtr<WebKitPoint> webkitConvertPointFromPageToNode(Node*, const WebKitPoint*) const;
+ PassRefPtrWillBeRawPtr<WebKitPoint> webkitConvertPointFromNodeToPage(Node*, const WebKitPoint*) const;
+
Console& console() const;
FrameConsole* frameConsole() const;
InputTypeTextMaxLength = 191,
InputTypePassword = 192,
InputTypePasswordMaxLength = 193,
+ SVGInstanceRoot = 194,
ShowModalDialog = 195,
PrefixedPageVisibility = 196,
CSSStyleSheetInsertRuleOptionalArg = 198, // Inconsistent with the specification and other browsers.
DeprecatedWebKitRepeatingRadialGradient = 264,
PrefixedImageSmoothingEnabled = 267,
UnprefixedImageSmoothingEnabled = 268,
+ PromiseConstructor = 270,
+ PromiseCast = 271,
+ PromiseReject = 272,
+ PromiseResolve = 273,
// The above items are available in M34 branch.
TextAutosizing = 274,
+ TextAutosizingLayout = 275,
HTMLAnchorElementPingAttribute = 276,
InsertAdjacentHTML = 278,
SVGClassName = 279,
// The above items are available in M35 branch.
SVGForeignObjectElement = 325,
+ PrefixedElementRequestPointerLock = 326,
SelectionSetPosition = 327,
AnimationPlayerFinishEvent = 328,
SVGSVGElementInXMLDocument = 329,
WindowOffscreenBuffering = 356,
WindowDefaultStatus = 357,
WindowDefaultstatus = 358,
+ PrefixedConvertPointFromPageToNode = 359,
+ PrefixedConvertPointFromNodeToPage = 360,
PrefixedTransitionEventConstructor = 361,
PrefixedMutationObserverConstructor = 362,
PrefixedIDBCursorConstructor = 363,
RangeDetach = 372,
HTMLTableElementVspace = 374,
HTMLTableElementHspace = 375,
+ PrefixedDocumentExitPointerLock = 376,
+ PrefixedDocumentPointerLockElement = 377,
PrefixedTouchRadiusX = 378,
PrefixedTouchRadiusY = 379,
PrefixedTouchRotationAngle = 380,
TextEncoderConstructor = 429,
TextEncoderEncode = 430,
TextDecoderConstructor = 431,
- TextDecoderDecode = 432,
+ TextDecoderDecode= 432,
FocusInOutEvent = 433,
MouseEventMovementX = 434,
MouseEventMovementY = 435,
+ MixedContentTextTrack = 436,
MixedContentRaw = 437,
MixedContentImage = 438,
MixedContentMedia = 439,
FormsSubmitted = 442,
TextInputEventOnInput = 443,
TextInputEventOnTextArea = 444,
- TextInputEventOnContentEditable = 445,
+ TextInputEventOnContentEditable= 445,
TextInputEventOnNotNode = 446,
WebkitBeforeTextInsertedOnInput = 447,
WebkitBeforeTextInsertedOnTextArea = 448,
[Replaceable] readonly attribute double devicePixelRatio;
+ [MeasureAs=PrefixedConvertPointFromPageToNode] WebKitPoint webkitConvertPointFromPageToNode([Default=Undefined] optional Node node,
+ [Default=Undefined] optional WebKitPoint p);
+ [MeasureAs=PrefixedConvertPointFromNodeToPage] WebKitPoint webkitConvertPointFromNodeToPage([Default=Undefined] optional Node node,
+ [Default=Undefined] optional WebKitPoint p);
+
[RuntimeEnabled=ApplicationCache, LogActivity=GetterOnly] readonly attribute ApplicationCache applicationCache;
[RuntimeEnabled=SessionStorage, LogActivity=GetterOnly, RaisesException=Getter] readonly attribute Storage sessionStorage;
void FormAssociatedElement::insertedInto(ContainerNode* insertionPoint)
{
- if (!m_formWasSetByParser || NodeTraversal::highestAncestorOrSelf(*insertionPoint) != NodeTraversal::highestAncestorOrSelf(*m_form.get()))
+ if (!m_formWasSetByParser || !m_form || NodeTraversal::highestAncestorOrSelf(*insertionPoint) != NodeTraversal::highestAncestorOrSelf(*m_form.get()))
resetFormOwner();
if (!insertionPoint->inDocument())
#endif
OwnPtrWillBeMember<ValidityState> m_validityState;
String m_customValidationMessage;
+ // Non-Oilpan: Even if m_formWasSetByParser is true, m_form can be null
+ // because parentNode is not a strong reference and |this| and m_form don't
+ // die together.
+ // Oilpan: If m_formWasSetByParser is true, m_form is always non-null.
bool m_formWasSetByParser;
};
didFinalizeFrame();
} else {
ASSERT(hasImageBuffer());
- m_imageBuffer->finalizeFrame();
+ m_imageBuffer->finalizeFrame(m_dirtyRect);
}
ASSERT(m_dirtyRect.isEmpty());
}
GestureEvent& gestureEvent = toGestureEvent(*event);
int listIndex = listIndexForEventTargetOption(gestureEvent);
if (listIndex >= 0) {
- if (!isDisabledFormControl())
+ if (!isDisabledFormControl()) {
updateSelectedState(listIndex, true, gestureEvent.shiftKey());
+ listBoxOnChange();
+ }
event->setDefaultHandled();
}
} else if (event->type() == EventTypeNames::mousedown && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
BaseChooserOnlyDateAndTimeInputType::~BaseChooserOnlyDateAndTimeInputType()
{
+ closeDateTimeChooser();
}
void BaseChooserOnlyDateAndTimeInputType::handleDOMActivateEvent(Event*)
BaseClickableWithKeyInputType::accessKeyAction(element(), sendMouseEvents);
}
-void BaseChooserOnlyDateAndTimeInputType::trace(Visitor* visitor)
-{
- visitor->trace(m_dateTimeChooser);
- BaseDateAndTimeInputType::trace(visitor);
- DateTimeChooserClient::trace(visitor);
-}
-
}
#endif
class BaseChooserOnlyDateAndTimeInputType : public BaseDateAndTimeInputType, public DateTimeChooserClient {
WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(BaseChooserOnlyDateAndTimeInputType);
-public:
- virtual void trace(Visitor*) OVERRIDE;
-
protected:
- explicit BaseChooserOnlyDateAndTimeInputType(HTMLInputElement& element) : BaseDateAndTimeInputType(element) { }
+ BaseChooserOnlyDateAndTimeInputType(HTMLInputElement& element) : BaseDateAndTimeInputType(element) { }
virtual ~BaseChooserOnlyDateAndTimeInputType();
private:
virtual void didChooseValue(double) OVERRIDE;
virtual void didEndChooser() OVERRIDE;
- RefPtrWillBeMember<DateTimeChooser> m_dateTimeChooser;
+ RefPtr<DateTimeChooser> m_dateTimeChooser;
};
}
return !document.settings() || document.settings()->fullscreenSupported();
}
+static bool deviceSupportsMouse(const Document& document)
+{
+ return !document.settings() || document.settings()->deviceSupportsMouse();
+}
+
MediaControls::MediaControls(HTMLMediaElement& mediaElement)
: HTMLDivElement(mediaElement.document())
, m_mediaElement(&mediaElement)
// Never hide for a media element without visual representation.
if (!mediaElement().hasVideo())
return false;
- // Don't hide if the controls are hovered or the mouse is over the video area.
+ // Don't hide if the mouse is over the controls.
+ const bool ignoreControlsHover = behaviorFlags & IgnoreControlsHover;
+ if (!ignoreControlsHover && m_panel->hovered())
+ return false;
+ // Don't hide if the mouse is over the video area.
const bool ignoreVideoHover = behaviorFlags & IgnoreVideoHover;
- if (m_panel->hovered() || (!ignoreVideoHover && m_isMouseOverControls))
+ if (!ignoreVideoHover && m_isMouseOverControls)
return false;
// Don't hide if focus is on the HTMLMediaElement or within the
// controls/shadow tree. (Perform the checks separately to avoid going
if (mediaElement().togglePlayStateWillPlay())
return;
- if (!shouldHideMediaControls(IgnoreFocus | IgnoreVideoHover))
+ unsigned behaviorFlags = IgnoreFocus | IgnoreVideoHover;
+ // FIXME: improve this check, see http://www.crbug.com/401177.
+ if (!deviceSupportsMouse(document())) {
+ behaviorFlags |= IgnoreControlsHover;
+ }
+ if (!shouldHideMediaControls(behaviorFlags))
return;
makeTransparent();
enum HideBehaviorFlags {
IgnoreVideoHover = 1 << 0,
- IgnoreFocus = 1 << 1
+ IgnoreFocus = 1 << 1,
+ IgnoreControlsHover = 1 << 2
};
bool shouldHideMediaControls(unsigned behaviorFlags = 0) const;
PickerIndicatorElement::~PickerIndicatorElement()
{
+ closePopup();
+ ASSERT(!m_chooser);
}
RenderObject* PickerIndicatorElement::createRenderer(RenderStyle*)
void PickerIndicatorElement::trace(Visitor* visitor)
{
visitor->trace(m_pickerIndicatorOwner);
- visitor->trace(m_chooser);
HTMLDivElement::trace(visitor);
- DateTimeChooserClient::trace(visitor);
}
}
class PagePopup;
class PickerIndicatorElement FINAL : public HTMLDivElement, public DateTimeChooserClient {
- WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(PickerIndicatorElement);
public:
// PickerIndicatorOwner implementer must call removePickerIndicatorOwner when
// it doesn't handle event, e.g. at destruction.
HTMLInputElement* hostInput();
RawPtrWillBeMember<PickerIndicatorOwner> m_pickerIndicatorOwner;
- RefPtrWillBeMember<DateTimeChooser> m_chooser;
+ RefPtr<DateTimeChooser> m_chooser;
bool m_isInOpenPopup;
};
return nullptr;
}
-PassRefPtrWillBeRawPtr<DateTimeChooser> EmptyChromeClient::openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&)
+PassRefPtr<DateTimeChooser> EmptyChromeClient::openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&)
{
- return nullptr;
+ return PassRefPtr<DateTimeChooser>();
}
void EmptyChromeClient::openTextDataListChooser(HTMLInputElement&)
virtual void enumerateChosenDirectory(FileChooser*) OVERRIDE { }
virtual PassOwnPtr<ColorChooser> createColorChooser(LocalFrame*, ColorChooserClient*, const Color&) OVERRIDE;
- virtual PassRefPtrWillBeRawPtr<DateTimeChooser> openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&) OVERRIDE;
+ virtual PassRefPtr<DateTimeChooser> openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&) OVERRIDE;
virtual void openTextDataListChooser(HTMLInputElement&) OVERRIDE;
virtual void runOpenPanel(LocalFrame*, PassRefPtr<FileChooser>) OVERRIDE;
bool isMainResource = type == FetchMainResource;
if (!isMainResource) {
String outgoingReferrer;
+ ReferrerPolicy referrerPolicy;
String outgoingOrigin;
if (request.httpReferrer().isNull()) {
outgoingReferrer = document->outgoingReferrer();
+ referrerPolicy = document->referrerPolicy();
outgoingOrigin = document->outgoingOrigin();
} else {
outgoingReferrer = request.httpReferrer();
+ referrerPolicy = request.referrerPolicy();
outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
}
- outgoingReferrer = SecurityPolicy::generateReferrerHeader(document->referrerPolicy(), request.url(), outgoingReferrer);
+ outgoingReferrer = SecurityPolicy::generateReferrerHeader(referrerPolicy, request.url(), outgoingReferrer);
if (outgoingReferrer.isEmpty())
request.clearHTTPReferrer();
- else if (!request.httpReferrer())
- request.setHTTPReferrer(Referrer(outgoingReferrer, document->referrerPolicy()));
+ else
+ request.setHTTPReferrer(Referrer(outgoingReferrer, referrerPolicy));
request.addHTTPOriginIfNeeded(AtomicString(outgoingOrigin));
}
return m_client->createColorChooser(frame, client, initialColor);
}
-PassRefPtrWillBeRawPtr<DateTimeChooser> Chrome::openDateTimeChooser(DateTimeChooserClient* client, const DateTimeChooserParameters& parameters)
+PassRefPtr<DateTimeChooser> Chrome::openDateTimeChooser(DateTimeChooserClient* client, const DateTimeChooserParameters& parameters)
{
notifyPopupOpeningObservers();
return m_client->openDateTimeChooser(client, parameters);
#include "core/page/FocusType.h"
#include "platform/Cursor.h"
#include "platform/HostWindow.h"
-#include "platform/heap/Handle.h"
#include "wtf/Forward.h"
namespace blink {
void print(LocalFrame*);
PassOwnPtr<ColorChooser> createColorChooser(LocalFrame*, ColorChooserClient*, const Color& initialColor);
- PassRefPtrWillBeRawPtr<DateTimeChooser> openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&);
+ PassRefPtr<DateTimeChooser> openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&);
void openTextDataListChooser(HTMLInputElement&);
void runOpenPanel(LocalFrame*, PassRefPtr<FileChooser>);
// returns true, if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
// - <datalist> UI for date/time input types regardless of
// ENABLE(INPUT_MULTIPLE_FIELDS_UI)
- virtual PassRefPtrWillBeRawPtr<DateTimeChooser> openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&) = 0;
+ virtual PassRefPtr<DateTimeChooser> openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&) = 0;
virtual void openTextDataListChooser(HTMLInputElement&) = 0;
RefPtr<Scrollbar> scrollbar = targetedEvent.hitTestResult().scrollbar();
const PlatformGestureEvent& gestureEvent = targetedEvent.event();
- if (!scrollbar) {
- FrameView* view = m_frame->view();
- scrollbar = view ? view->scrollbarAtPoint(gestureEvent.position()) : 0;
- }
-
if (scrollbar) {
bool eventSwallowed = scrollbar->gestureEvent(gestureEvent);
if (gestureEvent.type() == PlatformEvent::GestureTapDown && eventSwallowed)
modifierFlags |= PlatformEvent::ShiftKey;
PlatformEvent::Modifiers modifiers = static_cast<PlatformEvent::Modifiers>(modifierFlags);
+ HitTestResult currentHitTest = targetedEvent.hitTestResult();
+
// We use the adjusted position so the application isn't surprised to see a event with
// co-ordinates outside the target's bounds.
IntPoint adjustedPoint = m_frame->view()->windowToContents(gestureEvent.position());
- // Do a new hit-test at the (adjusted) gesture co-ordinates. This is necessary because
- // touch adjustment sometimes returns a different node than what hit testing would return
- // for the same point.
- // FIXME: Fix touch adjustment to avoid the need for a redundant hit test. http://crbug.com/398914
- HitTestResult newHitTest = hitTestResultInFrame(m_frame, adjustedPoint, HitTestRequest::ReadOnly);
-
PlatformMouseEvent fakeMouseMove(adjustedPoint, gestureEvent.globalPosition(),
NoButton, PlatformEvent::MouseMoved, /* clickCount */ 0,
modifiers, PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
- dispatchMouseEvent(EventTypeNames::mousemove, newHitTest.targetNode(), 0, fakeMouseMove, true);
+ dispatchMouseEvent(EventTypeNames::mousemove, currentHitTest.innerNode(), 0, fakeMouseMove, true);
// Do a new hit-test in case the mousemove event changed the DOM.
+ // Note that if the original hit test wasn't over an element (eg. was over a scrollbar) we
+ // don't want to re-hit-test because it may be in the wrong frame (and there's no way the page
+ // could have seen the event anyway).
// FIXME: Use a hit-test cache to avoid unnecessary hit tests. http://crbug.com/398920
- newHitTest = hitTestResultInFrame(m_frame, adjustedPoint, HitTestRequest::ReadOnly);
- m_clickNode = newHitTest.targetNode();
+ if (currentHitTest.innerNode())
+ currentHitTest = hitTestResultInFrame(m_frame, adjustedPoint, HitTestRequest::ReadOnly);
+ m_clickNode = currentHitTest.innerNode();
if (m_clickNode && m_clickNode->isTextNode())
m_clickNode = NodeRenderingTraversal::parent(m_clickNode.get());
PlatformMouseEvent fakeMouseDown(adjustedPoint, gestureEvent.globalPosition(),
LeftButton, PlatformEvent::MousePressed, gestureEvent.tapCount(),
modifiers, PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
- bool swallowMouseDownEvent = !dispatchMouseEvent(EventTypeNames::mousedown, newHitTest.targetNode(), gestureEvent.tapCount(), fakeMouseDown, true);
+ bool swallowMouseDownEvent = !dispatchMouseEvent(EventTypeNames::mousedown, currentHitTest.innerNode(), gestureEvent.tapCount(), fakeMouseDown, true);
if (!swallowMouseDownEvent)
swallowMouseDownEvent = handleMouseFocus(fakeMouseDown);
if (!swallowMouseDownEvent)
- swallowMouseDownEvent = handleMousePressEvent(MouseEventWithHitTestResults(fakeMouseDown, newHitTest));
+ swallowMouseDownEvent = handleMousePressEvent(MouseEventWithHitTestResults(fakeMouseDown, currentHitTest));
// FIXME: Use a hit-test cache to avoid unnecessary hit tests. http://crbug.com/398920
- newHitTest = hitTestResultInFrame(m_frame, adjustedPoint, HitTestRequest::ReadOnly);
+ if (currentHitTest.innerNode())
+ currentHitTest = hitTestResultInFrame(m_frame, adjustedPoint, HitTestRequest::ReadOnly);
PlatformMouseEvent fakeMouseUp(adjustedPoint, gestureEvent.globalPosition(),
LeftButton, PlatformEvent::MouseReleased, gestureEvent.tapCount(),
modifiers, PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
- bool swallowMouseUpEvent = !dispatchMouseEvent(EventTypeNames::mouseup, newHitTest.targetNode(), gestureEvent.tapCount(), fakeMouseUp, false);
+ bool swallowMouseUpEvent = !dispatchMouseEvent(EventTypeNames::mouseup, currentHitTest.innerNode(), gestureEvent.tapCount(), fakeMouseUp, false);
bool swallowClickEvent = false;
if (m_clickNode) {
- if (newHitTest.targetNode()) {
- Node* clickTargetNode = newHitTest.targetNode()->commonAncestor(*m_clickNode, parentForClickEvent);
+ if (currentHitTest.innerNode()) {
+ Node* clickTargetNode = currentHitTest.innerNode()->commonAncestor(*m_clickNode, parentForClickEvent);
swallowClickEvent = !dispatchMouseEvent(EventTypeNames::click, clickTargetNode, gestureEvent.tapCount(), fakeMouseUp, true);
}
m_clickNode = nullptr;
}
if (!swallowMouseUpEvent)
- swallowMouseUpEvent = handleMouseReleaseEvent(MouseEventWithHitTestResults(fakeMouseUp, newHitTest));
+ swallowMouseUpEvent = handleMouseReleaseEvent(MouseEventWithHitTestResults(fakeMouseUp, currentHitTest));
return swallowMouseDownEvent | swallowMouseUpEvent | swallowClickEvent;
}
// FIXME: We should not do a rect-based hit-test if touch adjustment is disabled.
HitTestResult hitTestResult = hitTestResultAtPoint(hitTestPoint, hitType | HitTestRequest::ReadOnly, touchRadius);
+ // Hit-test the main frame scrollbars (in addition to the child-frame and RenderLayer
+ // scroll bars checked by the hit-test code.
+ if (!hitTestResult.scrollbar()) {
+ if (FrameView* view = m_frame->view()) {
+ hitTestResult.setScrollbar(view->scrollbarAtPoint(gestureEvent.position()));
+ }
+ }
+
// Adjust the location of the gesture to the most likely nearby node, as appropriate for the
// type of event.
PlatformGestureEvent adjustedEvent = gestureEvent;
applyTouchAdjustment(&adjustedEvent, &hitTestResult);
+ // Do a new hit-test at the (adjusted) gesture co-ordinates. This is necessary because
+ // rect-based hit testing and touch adjustment sometimes return a different node than
+ // what a point-based hit test would return for the same point.
+ // FIXME: Fix touch adjustment to avoid the need for a redundant hit test. http://crbug.com/398914
+ if (shouldApplyTouchAdjustment(gestureEvent)) {
+ LocalFrame* hitFrame = hitTestResult.innerNodeFrame();
+ if (!hitFrame)
+ hitFrame = m_frame;
+ hitTestResult = hitTestResultInFrame(hitFrame, hitFrame->view()->windowToContents(adjustedEvent.position()), hitType | HitTestRequest::ReadOnly);
+ // FIXME: HitTest entry points should really check for main frame scrollbars themselves.
+ if (!hitTestResult.scrollbar()) {
+ if (FrameView* view = m_frame->view()) {
+ hitTestResult.setScrollbar(view->scrollbarAtPoint(gestureEvent.position()));
+ }
+ }
+ }
+
// Now apply hover/active state to the final target.
// FIXME: This is supposed to send mouseenter/mouseleave events, but doesn't because we
// aren't passing a PlatformMouseEvent.
String text = this->text();
int len = text.length();
TextBreakIterator* iterator = wordBreakIterator(text, 0, len);
+
+ // FIXME: When http://crbug.com/411764 is fixed, replace this with an ASSERT.
+ if (!iterator)
+ return;
+
int pos = iterator->first();
while (pos >= 0 && pos < len) {
int next = iterator->next();
return;
const RenderBox& box = toRenderBox(renderer);
- LayoutRect clipRect(toPoint(m_paintOffset), box.layer()->size());
- if (m_clipped) {
- m_clipRect.intersect(clipRect);
+
+ // Do not clip scroll layer contents because the compositor expects the whole layer
+ // to be always invalidated in-time.
+ if (box.usesCompositedScrolling()) {
+ ASSERT(!m_clipped); // The box should establish paint invalidation container, so no m_clipped inherited.
} else {
- m_clipRect = clipRect;
- m_clipped = true;
+ LayoutRect clipRect(toPoint(m_paintOffset), box.layer()->size());
+ if (m_clipped) {
+ m_clipRect.intersect(clipRect);
+ } else {
+ m_clipRect = clipRect;
+ m_clipped = true;
+ }
}
+
m_paintOffset -= box.scrolledContentOffset();
}
flipForWritingMode(paintRect);
paintRect.move(-scrolledContentOffset()); // For overflow:auto/scroll/hidden.
- // Do not clip scroll layer contents to reduce the number of repaints while scrolling.
+ // Do not clip scroll layer contents because the compositor expects the whole layer
+ // to be always invalidated in-time.
if (usesCompositedScrolling()) {
flipForWritingMode(paintRect);
return;
// FIXME: For now just support fixed heights. Eventually should support percentage heights as well.
if (!logicalHeightLength.isFixed()) {
- LayoutUnit fillFallbackExtent = containingBlockStyle->isHorizontalWritingMode() ? view()->frameView()->visibleHeight() : view()->frameView()->visibleWidth();
+ LayoutUnit fillFallbackExtent = containingBlockStyle->isHorizontalWritingMode()
+ ? view()->frameView()->unscaledVisibleContentSize().height()
+ : view()->frameView()->unscaledVisibleContentSize().width();
LayoutUnit fillAvailableExtent = containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding);
return std::min(fillAvailableExtent, fillFallbackExtent);
}
LayoutUnit RenderBox::availableLogicalHeightUsing(const Length& h, AvailableLogicalHeightType heightType) const
{
if (isRenderView())
- return isHorizontalWritingMode() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth();
+ return isHorizontalWritingMode() ? toRenderView(this)->frameView()->unscaledVisibleContentSize().height() : toRenderView(this)->frameView()->unscaledVisibleContentSize().width();
// We need to stop here, since we don't want to increase the height of the table
// artificially. We're going to rely on this cell getting expanded to some new
RenderLayer* RenderLayer::enclosingTransformedAncestor() const
{
RenderLayer* curr = parent();
- while (curr && !curr->isRootLayer() && !curr->transform())
+ while (curr && !curr->isRootLayer() && !curr->renderer()->hasTransform())
curr = curr->parent();
return curr;
box().setPreviousPaintInvalidationRect(box().boundsRectForPaintInvalidation(repaintContainer));
// Update regions, scrolling may change the clip of a particular region.
frameView->updateAnnotatedRegions();
- // FIXME: We shouldn't call updateWidgetPositions() here since it might tear down the render tree,
- // for now we just crash to avoid allowing an attacker to use after free.
- frameView->updateWidgetPositions();
- RELEASE_ASSERT(frameView->renderView());
+ frameView->setNeedsUpdateWidgetPositions();
updateCompositingLayersAfterScroll();
}
return layer->scrollsOverflow()
&& layer->compositor()->acceleratedCompositingForOverflowScrollEnabled()
&& !layer->hasDescendantWithClipPath()
- && !layer->hasAncestorWithClipPath();
+ && !layer->hasAncestorWithClipPath()
+ && !layer->renderer()->style()->hasBorderRadius();
}
void RenderLayerScrollableArea::updateNeedsCompositedScrolling()
box->addFocusRingRects(layerFocusRingRects, LayoutPoint(), box);
for (size_t i = 0; i < layerFocusRingRects.size(); ++i) {
FloatQuad quadInBox = box->localToContainerQuad(FloatRect(layerFocusRingRects[i]), paintContainer);
- rects.append(pixelSnappedIntRect(LayoutRect(quadInBox.boundingBox())));
+ FloatRect rect = quadInBox.boundingBox();
+ // Floor the location instead of using pixelSnappedIntRect to match the !hasLayer() path.
+ // FIXME: roundedIntSize matches pixelSnappedIntRect in other places of addFocusRingRects
+ // because we always floor the offset.
+ // This assumption is fragile and should be replaced by better solution.
+ rects.append(IntRect(flooredIntPoint(rect.location()), roundedIntSize(rect.size())));
}
} else {
FloatPoint pos(additionalOffset);
- pos.move(box->locationOffset()); // FIXME: Snap offsets? crbug.com/350474
+ pos.move(box->locationOffset());
box->addFocusRingRects(rects, flooredIntPoint(pos), paintContainer);
}
} else {
void repaint()
{
- m_object->invalidatePaintUsingContainer(m_repaintContainer, m_rect, InvalidationSelection);
+ m_object->invalidatePaintUsingContainer(m_repaintContainer, enclosingIntRect(m_rect), InvalidationSelection);
}
LayoutRect rect() const { return m_rect; }
// repaintContainer as the render object. Find out why it does that and fix.
if (m_repaintContainer && m_repaintContainer->layer()->groupedMapping())
RenderLayer::mapRectToPaintInvalidationBacking(m_repaintContainer, m_repaintContainer, repaintRect);
- m_object->invalidatePaintUsingContainer(m_repaintContainer, repaintRect, InvalidationSelection);
+ m_object->invalidatePaintUsingContainer(m_repaintContainer, enclosingIntRect(repaintRect), InvalidationSelection);
}
RenderBlock* block() const { return toRenderBlock(m_object); }
#include "platform/LengthFunctions.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/fonts/FontCache.h"
+#include "platform/geometry/TransformState.h"
#include "platform/graphics/GraphicsContext.h"
#include "wtf/CurrentTime.h"
#include "wtf/text/StringBuilder.h"
m_overflowControlsHostLayer->setPosition(IntPoint(-m_overflowControlsClippingLayer->offsetFromRenderer()));
} else {
- ASSERT(m_owningLayer.transformAncestor() == compositingStackingContext->transformAncestor());
- LayoutPoint localOffsetToTransformedAncestor = m_owningLayer.computeOffsetFromTransformedAncestor();
- LayoutPoint compositingStackingContextOffsetToTransformedAncestor = compositingStackingContext->computeOffsetFromTransformedAncestor();
-
- m_overflowControlsHostLayer->setPosition(FloatPoint(localOffsetToTransformedAncestor - compositingStackingContextOffsetToTransformedAncestor));
+ // The controls are in the same 2D space as the compositing container, so we can map them into the space of the container.
+ TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint());
+ m_owningLayer.renderer()->mapLocalToContainer(compositingStackingContext->renderer(), transformState, ApplyContainerFlip);
+ transformState.flatten();
+ LayoutPoint offsetFromStackingContainer = LayoutPoint(transformState.lastPlanarPoint());
+ m_overflowControlsHostLayer->setPosition(FloatPoint(offsetFromStackingContainer));
}
} else {
m_overflowControlsHostLayer->setPosition(FloatPoint());
Color bgColor(Color::transparent);
if (contentLayerSupportsDirectBackgroundComposition(renderer())) {
bgColor = rendererBackgroundColor();
+ hasPaintedContent = false;
}
contentLayer->setBackgroundColor(bgColor.rgb());
}
&& (compositingAncestorLayer->compositedLayerMapping()->mainGraphicsLayer()->drawsContent()
|| compositingAncestorLayer->compositedLayerMapping()->paintsIntoCompositedAncestor());
- if (paintsIntoCompositedAncestor() != previousPaintsIntoCompositedAncestor)
- compositor()->paintInvalidationOnCompositingChange(&m_owningLayer);
-
- // FIXME: this is bogus. We need to make this assignment before the check above.
m_requiresOwnBackingStoreForAncestorReasons = !canPaintIntoAncestor;
+ if (paintsIntoCompositedAncestor() != previousPaintsIntoCompositedAncestor) {
+ // Back out the change temporarily while invalidating with respect to the old container.
+ m_requiresOwnBackingStoreForAncestorReasons = !m_requiresOwnBackingStoreForAncestorReasons;
+ compositor()->paintInvalidationOnCompositingChange(&m_owningLayer);
+ m_requiresOwnBackingStoreForAncestorReasons = !m_requiresOwnBackingStoreForAncestorReasons;
+ }
return m_requiresOwnBackingStoreForAncestorReasons != previousRequiresOwnBackingStoreForAncestorReasons;
}
|| renderer->hasReflection()
|| renderer->hasFilter();
- if (paintsIntoCompositedAncestor() != previousPaintsIntoCompositedAncestor)
+ if (paintsIntoCompositedAncestor() != previousPaintsIntoCompositedAncestor) {
+ // Back out the change temporarily while invalidating with respect to the old container.
+ m_requiresOwnBackingStoreForIntrinsicReasons = !m_requiresOwnBackingStoreForIntrinsicReasons;
compositor()->paintInvalidationOnCompositingChange(&m_owningLayer);
+ m_requiresOwnBackingStoreForIntrinsicReasons = !m_requiresOwnBackingStoreForIntrinsicReasons;
+ }
return m_requiresOwnBackingStoreForIntrinsicReasons != previousRequiresOwnBackingStoreForIntrinsicReasons;
}
const RenderLayer* parent = layer->parent();
properties.opacityAncestor = parent->isTransparent() ? parent : parent->opacityAncestor();
- properties.transformAncestor = parent->transform() ? parent : parent->transformAncestor();
+ properties.transformAncestor = parent->hasTransform() ? parent : parent->transformAncestor();
properties.filterAncestor = parent->hasFilter() ? parent : parent->filterAncestor();
if (info.hasAncestorWithClipOrOverflowClip) {
if (compositingInputs.transformAncestor != squashingLayerCompositingInputs.transformAncestor)
return CompositingReasonSquashingTransformAncestorMismatch;
- if (compositingInputs.filterAncestor != squashingLayerCompositingInputs.filterAncestor)
- return CompositingReasonSquashingFilterAncestorMismatch;
+ if (layer->hasFilter() || compositingInputs.filterAncestor != squashingLayerCompositingInputs.filterAncestor)
+ return CompositingReasonSquashingFilterMismatch;
return CompositingReasonNone;
}
ASSERT(sourceGraphic);
builder.setSourceGraphic(sourceGraphic);
RefPtr<ImageFilter> imageFilter = builder.build(filterData->builder->lastEffect(), ColorSpaceDeviceRGB);
- FloatRect boundaries = enclosingIntRect(filterData->boundaries);
+ FloatRect boundaries = filterData->boundaries;
context->save();
FloatSize deviceSize = context->getCTM().mapSize(boundaries.size());
float scale = sqrtf(FilterEffect::maxFilterArea() / scaledArea);
context->scale(scale, scale);
}
- // Clip drawing of filtered image to primitive boundaries.
- context->clipRect(boundaries);
+ // Clip drawing of filtered image to the minimum required paint rect.
+ FilterEffect* lastEffect = filterData->builder->lastEffect();
+ context->clipRect(lastEffect->determineAbsolutePaintRect(lastEffect->maxEffectRect()));
if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
// Get boundaries in device coords.
// FIXME: See crbug.com/382491. Is the use of getCTM OK here, given it does not include device
WebInspector.targetManager.observeTargets(this);
WebInspector.settings.showUAShadowDOM.addChangeListener(this._showUAShadowDOMChanged.bind(this));
WebInspector.targetManager.addModelListener(WebInspector.DOMModel, WebInspector.DOMModel.Events.DocumentUpdated, this._documentUpdatedEvent, this);
- WebInspector.targetManager.addModelListener(WebInspector.DOMModel, WebInspector.CSSStyleModel.Events.ModelWasEnabled, this._updateSidebars, this);
+ WebInspector.targetManager.addModelListener(WebInspector.CSSStyleModel, WebInspector.CSSStyleModel.Events.ModelWasEnabled, this._updateSidebars, this);
}
WebInspector.ElementsPanel.prototype = {
}
}
- var element = event.target.enclosingNodeOrSelfWithClass("elements-tree-outline");
- if (!element)
- return;
var treeOutline = null;
for (var i = 0; i < this._treeOutlines.length; ++i) {
- if (this._treeOutlines[i].element === element)
+ if (this._treeOutlines[i].selectedDOMNode() === this._lastValidSelectedNode)
treeOutline = this._treeOutlines[i];
}
if (!treeOutline)
_contextMenuEventFired: function(event)
{
var treeElement = this._treeElementFromEvent(event);
- if (!treeElement)
+ if (!treeElement || treeElement.treeOutline !== this)
return;
var contextMenu = new WebInspector.ContextMenu(event);
populateContextMenu: function(contextMenu, event)
{
var treeElement = this._treeElementFromEvent(event);
- if (!treeElement)
+ if (!treeElement || treeElement.treeOutline !== this)
return;
var isPseudoElement = !!treeElement._node.pseudoType();
this.hide(true);
}
+ delete this._isHidden;
this._anchorElement = element;
this._spectrum.setColor(color);
document.addEventListener("mousedown", this._hideProxy, false);
window.addEventListener("blur", this._hideProxy, false);
+ window.addEventListener("resize", this._hideProxy, false);
return true;
},
*/
hide: function(commitEdit)
{
- if (!this._popover.isShowing())
+ if (this._isHidden)
return;
+ this._isHidden = true;
this._popover.hide();
document.removeEventListener("mousedown", this._hideProxy, false);
window.removeEventListener("blur", this._hideProxy, false);
+ window.removeEventListener("resize", this._hideProxy, false);
this.dispatchEventToListeners(WebInspector.SpectrumPopupHelper.Events.Hidden, !!commitEdit);
background-color: pink;
}
-input.list-column-editor,
-select.list-column-editor {
+input.list-column-editor {
border: 1px solid rgb(213, 213, 213);
border-radius: 2px;
color: #444444;
}
select.list-column-editor {
padding: 2px;
+ margin-left: 0px;
}
.settings-tab .settings-list .settings-list-item .file-system-path {
console.timeStamp("Main._loaded");
// FIXME: Make toolbox a real app.
- if (WebInspector.queryParam("toolbox")) {
- new WebInspector.Toolbox();
+ if (WebInspector.queryParam("toolbox"))
return;
- }
this._createSettings();
this._createModuleManager();
{
this._unavailableSplashScreenElement.classList.toggle("hidden", WebInspector.overridesSupport.canEmulate());
this._tabbedPane.element.classList.toggle("hidden", !WebInspector.overridesSupport.emulationEnabled());
- this._splashScreenElement.classList.toggle("hidden", WebInspector.overridesSupport.emulationEnabled());
+ this._splashScreenElement.classList.toggle("hidden", WebInspector.overridesSupport.emulationEnabled() || !WebInspector.overridesSupport.canEmulate());
},
__proto__: WebInspector.VBox.prototype
*/
WebInspector.Toolbox = function()
{
- if (!window.opener)
+ if (!window.opener || !WebInspector.queryParam("toolbox"))
return;
WebInspector.zoomManager = new WebInspector.ZoomManager(window.opener.InspectorFrontendHost);
});
this._dataGrid = new WebInspector.SortableDataGrid(columns);
+ this._dataGrid.setStickToBottom(true);
this._updateColumns();
this._dataGrid.setName("networkLog");
this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last);
}
this._removeAllNodeHighlights();
- var wasScrolledToLastRow = this._dataGrid.isScrolledToLastRow();
var boundariesChanged = false;
var calculator = this.calculator();
if (calculator.updateBoundariesForEventTime) {
this._staleRequestIds = {};
this._updateSummaryBar();
- if (wasScrolledToLastRow)
- this._dataGrid.scrollToLastRow();
},
_onRecordButtonClicked: function()
{
if (itemId === this._editingId)
return;
- event.consume();
console.assert(!this._editingId);
this._editingId = validItemId;
var listItem = this.itemForId(validItemId);
this._codeMirror.on("beforeSelectionChange", this._beforeSelectionChange.bind(this));
this._codeMirror.on("scroll", this._scroll.bind(this));
this._codeMirror.on("focus", this._focus.bind(this));
+ this._codeMirror.on("keyHandled", this._onKeyHandled.bind(this));
this.element.addEventListener("contextmenu", this._contextMenu.bind(this), false);
/**
* @this {WebInspector.CodeMirrorTextEditor}
WebInspector.CodeMirrorTextEditor.MaxEditableTextSize = 1024 * 1024 * 10;
WebInspector.CodeMirrorTextEditor.prototype = {
+ _onKeyHandled: function()
+ {
+ WebInspector.shortcutRegistry.dismissPendingShortcutAction();
+ },
+
_onAutoAppendedSpaces: function()
{
this._autoAppendedSpaces = this._autoAppendedSpaces || [];
return this._scrollContainer;
},
- /**
- * @return {boolean}
- */
- isScrolledToLastRow: function()
- {
- return this._scrollContainer.isScrolledToBottom();
- },
-
- scrollToLastRow: function()
- {
- this._scrollContainer.scrollTop = this._scrollContainer.scrollHeight - this._scrollContainer.offsetHeight;
- },
-
_positionResizers: function()
{
var headerTableColumns = this._headerTableColumnGroup.children;
this._defaultKeyToActions.put(String(descriptor.key), actionId);
},
- /**
- * @param {!Event} event
- */
- _onInput: function(event)
+ dismissPendingShortcutAction: function()
{
if (this._pendingActionTimer) {
clearTimeout(this._pendingActionTimer);
_registerBindings: function()
{
- document.addEventListener("input", this._onInput.bind(this), true);
+ document.addEventListener("input", this.dismissPendingShortcutAction.bind(this), true);
var extensions = self.runtime.extensions(WebInspector.ActionDelegate);
extensions.forEach(registerExtension, this);
/** @type {?Node} */
this._hiddenWheelTarget = null;
+ /** @type {boolean} */
+ this._stickToBottom = false;
+ /** @type {boolean} */
+ this._atBottom = true;
+ /** @type {number} */
+ this._lastScrollTop = 0;
+
this.setRootNode(new WebInspector.ViewportDataGridNode());
}
*/
onResize: function()
{
+ if (this._stickToBottom && this._atBottom)
+ this._scrollContainer.scrollTop = this._scrollContainer.scrollHeight - this._scrollContainer.clientHeight;
this.scheduleUpdate();
},
/**
+ * @param {boolean} stick
+ */
+ setStickToBottom: function(stick)
+ {
+ this._stickToBottom = stick;
+ },
+
+ /**
* @param {?Event} event
*/
_onWheel: function(event)
*/
_onScroll: function(event)
{
- this.scheduleUpdate();
+ this._atBottom = this._scrollContainer.isScrolledToBottom();
+ if (this._lastScrollTop !== this._scrollContainer.scrollTop)
+ this.scheduleUpdate();
},
/**
},
/**
- * @param {number} scrollHeight
+ * @param {number} clientHeight
* @param {number} scrollTop
* @return {{topPadding: number, bottomPadding: number, visibleNodes: !Array.<!WebInspector.ViewportDataGridNode>, offset: number}}
*/
- _calculateVisibleNodes: function(scrollHeight, scrollTop)
+ _calculateVisibleNodes: function(clientHeight, scrollTop)
{
var nodes = this._rootNode.children;
if (this._inline)
var start = i;
var topPadding = y;
- for (; i < size && y < scrollTop + scrollHeight; ++i)
+ for (; i < size && y < scrollTop + clientHeight; ++i)
y += nodes[i].nodeSelfHeight();
var end = i;
return {topPadding: topPadding, bottomPadding: bottomPadding, visibleNodes: nodes.slice(start, end), offset: start};
},
+ /**
+ * @return {number}
+ */
+ _contentHeight: function()
+ {
+ var nodes = this._rootNode.children;
+ var result = 0;
+ for (var i = 0, size = nodes.length; i < size; ++i)
+ result += nodes[i].nodeSelfHeight();
+ return result;
+ },
+
_update: function()
{
this._updateScheduled = false;
- var viewportState = this._calculateVisibleNodes(this._scrollContainer.offsetHeight, this._scrollContainer.scrollTop);
+ var clientHeight = this._scrollContainer.clientHeight;
+ var scrollTop = this._scrollContainer.scrollTop;
+ var currentScrollTop = scrollTop;
+ var maxScrollTop = Math.max(0, this._contentHeight() - clientHeight);
+ if (this._stickToBottom && this._atBottom)
+ scrollTop = maxScrollTop;
+ scrollTop = Math.min(maxScrollTop, scrollTop);
+ this._atBottom = scrollTop === maxScrollTop;
+
+ var viewportState = this._calculateVisibleNodes(clientHeight, scrollTop);
var visibleNodes = viewportState.visibleNodes;
var visibleNodesSet = Set.fromArray(visibleNodes);
}
this.setVerticalPadding(viewportState.topPadding, viewportState.bottomPadding);
+ this._lastScrollTop = scrollTop;
+ if (scrollTop !== currentScrollTop)
+ this._scrollContainer.scrollTop = scrollTop;
this._visibleNodes = visibleNodes;
},
#include "modules/filesystem/DOMFileSystemBase.h"
#include "core/dom/ExecutionContext.h"
+#include "core/fileapi/File.h"
#include "core/fileapi/FileError.h"
#include "core/html/VoidCallback.h"
#include "modules/filesystem/DOMFilePath.h"
return false;
}
+PassRefPtrWillBeRawPtr<File> DOMFileSystemBase::createFile(const FileMetadata& metadata, const KURL& fileSystemURL, FileSystemType type, const String name)
+{
+ // For regular filesystem types (temporary or persistent), we should not cache file metadata as it could change File semantics.
+ // For other filesystem types (which could be platform-specific ones), there's a chance that the files are on remote filesystem.
+ // If the port has returned metadata just pass it to File constructor (so we may cache the metadata).
+ // FIXME: We should use the snapshot metadata for all files.
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17746
+ if (type == FileSystemTypeTemporary || type == FileSystemTypePersistent)
+ return File::createForFileSystemFile(metadata.platformPath, name);
+
+ if (!metadata.platformPath.isEmpty()) {
+ // If the platformPath in the returned metadata is given, we create a File object for the path.
+ File::UserVisibility userVisibility = (type == FileSystemTypeExternal) ? File::IsUserVisible : File::IsNotUserVisible;
+ return File::createForFileSystemFile(name, metadata, userVisibility);
+ }
+
+ return File::createForFileSystemFile(fileSystemURL, metadata);
+}
+
void DOMFileSystemBase::getMetadata(const EntryBase* entry, PassOwnPtr<MetadataCallback> successCallback, PassOwnPtr<ErrorCallback> errorCallback, SynchronousType synchronousType)
{
if (!fileSystem()) {
class EntryBase;
class EntryCallback;
class ErrorCallback;
+class File;
class FileError;
+struct FileMetadata;
class MetadataCallback;
class ExecutionContext;
class SecurityOrigin;
KURL createFileSystemURL(const String& fullPath) const;
static bool pathToAbsolutePath(FileSystemType, const EntryBase*, String path, String& absolutePath);
static bool pathPrefixToFileSystemType(const String& pathPrefix, FileSystemType&);
+ static PassRefPtrWillBeRawPtr<File> createFile(const FileMetadata&, const KURL& fileSystemURL, FileSystemType, const String name);
// Actual FileSystem API implementations. All the validity checks on virtual paths are done at this level.
void getMetadata(const EntryBase*, PassOwnPtr<MetadataCallback>, PassOwnPtr<ErrorCallback>, SynchronousType = Asynchronous);
--- /dev/null
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "modules/filesystem/DOMFileSystemBase.h"
+
+#include "core/fileapi/File.h"
+#include "public/platform/Platform.h"
+#include "public/platform/WebUnitTestSupport.h"
+#include <gtest/gtest.h>
+
+
+namespace blink {
+
+class DOMFileSystemBaseTest : public ::testing::Test {
+public:
+ DOMFileSystemBaseTest()
+ {
+ m_filePath = Platform::current()->unitTestSupport()->webKitRootDir();
+ m_filePath.append("/Source/modules/filesystem/DOMFileSystemBaseTest.cpp");
+ getFileMetadata(m_filePath, m_fileMetadata);
+ m_fileMetadata.platformPath = m_filePath;
+ }
+
+protected:
+ String m_filePath;
+ FileMetadata m_fileMetadata;
+};
+
+
+TEST_F(DOMFileSystemBaseTest, externalFilesystemFilesAreUserVisible)
+{
+ KURL rootUrl = DOMFileSystemBase::createFileSystemRootURL("http://chromium.org/", FileSystemTypeExternal);
+
+ RefPtrWillBeRawPtr<File> file = DOMFileSystemBase::createFile(m_fileMetadata, rootUrl, FileSystemTypeExternal, "DOMFileSystemBaseTest.cpp");
+ EXPECT_TRUE(file);
+ EXPECT_TRUE(file->hasBackingFile());
+ EXPECT_EQ(File::IsUserVisible, file->userVisibility());
+ EXPECT_EQ("DOMFileSystemBaseTest.cpp", file->name());
+ EXPECT_EQ(m_filePath, file->path());
+}
+
+TEST_F(DOMFileSystemBaseTest, temporaryFilesystemFilesAreNotUserVisible)
+{
+ KURL rootUrl = DOMFileSystemBase::createFileSystemRootURL("http://chromium.org/", FileSystemTypeTemporary);
+
+ RefPtrWillBeRawPtr<File> file = DOMFileSystemBase::createFile(m_fileMetadata, rootUrl, FileSystemTypeTemporary, "UserVisibleName.txt");
+ EXPECT_TRUE(file);
+ EXPECT_TRUE(file->hasBackingFile());
+ EXPECT_EQ(File::IsNotUserVisible, file->userVisibility());
+ EXPECT_EQ("UserVisibleName.txt", file->name());
+ EXPECT_EQ(m_filePath, file->path());
+}
+
+TEST_F(DOMFileSystemBaseTest, persistentFilesystemFilesAreNotUserVisible)
+{
+ KURL rootUrl = DOMFileSystemBase::createFileSystemRootURL("http://chromium.org/", FileSystemTypePersistent);
+
+ RefPtrWillBeRawPtr<File> file = DOMFileSystemBase::createFile(m_fileMetadata, rootUrl, FileSystemTypePersistent, "UserVisibleName.txt");
+ EXPECT_TRUE(file);
+ EXPECT_TRUE(file->hasBackingFile());
+ EXPECT_EQ(File::IsNotUserVisible, file->userVisibility());
+ EXPECT_EQ("UserVisibleName.txt", file->name());
+ EXPECT_EQ(m_filePath, file->path());
+}
+
+} // namespace blink
+
+
+
// *after* we've coined a File with a new handle that has the correct type set on it. This allows the
// blob storage system to track when a temp file can and can't be safely deleted.
- // For regular filesystem types (temporary or persistent), we should not cache file metadata as it could change File semantics.
- // For other filesystem types (which could be platform-specific ones), there's a chance that the files are on remote filesystem.
- // If the port has returned metadata just pass it to File constructor (so we may cache the metadata).
- // FIXME: We should use the snapshot metadata for all files.
- // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17746
- if (m_type == FileSystemTypeTemporary || m_type == FileSystemTypePersistent) {
- m_result->m_file = File::createForFileSystemFile(metadata.platformPath, m_name);
- } else if (!metadata.platformPath.isEmpty()) {
- // If the platformPath in the returned metadata is given, we create a File object for the path.
- m_result->m_file = File::createForFileSystemFile(m_name, metadata).get();
- } else {
- // Otherwise create a File from the FileSystem URL.
- m_result->m_file = File::createForFileSystemFile(m_url, metadata).get();
- }
+ m_result->m_file = DOMFileSystemBase::createFile(metadata, m_url, m_type, m_name);
}
virtual bool shouldBlockUntilCompletion() const OVERRIDE
// *after* we've coined a File with a new handle that has the correct type set on it. This allows the
// blob storage system to track when a temp file can and can't be safely deleted.
- // For regular filesystem types (temporary or persistent), we should not cache file metadata as it could change File semantics.
- // For other filesystem types (which could be platform-specific ones), there's a chance that the files are on remote filesystem. If the port has returned metadata just pass it to File constructor (so we may cache the metadata).
- // FIXME: We should use the snapshot metadata for all files.
- // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17746
- if (m_fileSystem->type() == FileSystemTypeTemporary || m_fileSystem->type() == FileSystemTypePersistent) {
- handleEventOrScheduleCallback(m_successCallback.release(), File::createForFileSystemFile(metadata.platformPath, m_name));
- } else if (!metadata.platformPath.isEmpty()) {
- // If the platformPath in the returned metadata is given, we create a File object for the path.
- handleEventOrScheduleCallback(m_successCallback.release(), File::createForFileSystemFile(m_name, metadata));
- } else {
- // Otherwise create a File from the FileSystem URL.
- handleEventOrScheduleCallback(m_successCallback.release(), File::createForFileSystemFile(m_url, metadata));
- }
+ handleEventOrScheduleCallback(m_successCallback.release(), DOMFileSystemBase::createFile(metadata, m_url, m_fileSystem->type(), m_name));
}
// VoidCallbacks --------------------------------------------------------------
'vibration/testing/InternalsVibration.h',
],
'modules_unittest_files': [
+ 'filesystem/DOMFileSystemBaseTest.cpp',
'indexeddb/IDBKeyPathTest.cpp',
'indexeddb/IDBRequestTest.cpp',
'indexeddb/IDBTransactionTest.cpp',
#include "platform/PlatformExport.h"
#include "platform/geometry/IntRect.h"
-#include "platform/heap/Handle.h"
#include "wtf/RefCounted.h"
#include "wtf/text/WTFString.h"
bool isAnchorElementRTL;
};
-class PLATFORM_EXPORT DateTimeChooser : public RefCountedWillBeGarbageCollectedFinalized<DateTimeChooser> {
+// For pickers like color pickers and date pickers.
+class PLATFORM_EXPORT DateTimeChooser : public RefCounted<DateTimeChooser> {
public:
virtual ~DateTimeChooser();
virtual void endChooser() = 0;
- virtual void trace(Visitor*) { }
};
} // namespace blink
namespace blink {
-DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(DateTimeChooserClient)
+DateTimeChooserClient::~DateTimeChooserClient()
+{
+}
} // namespace blink
#define DateTimeChooserClient_h
#include "platform/PlatformExport.h"
-#include "platform/heap/Handle.h"
#include "wtf/text/WTFString.h"
namespace blink {
-class PLATFORM_EXPORT DateTimeChooserClient : public WillBeGarbageCollectedMixin {
- DECLARE_EMPTY_VIRTUAL_DESTRUCTOR_WILL_BE_REMOVED(DateTimeChooserClient);
+class PLATFORM_EXPORT DateTimeChooserClient {
public:
+ virtual ~DateTimeChooserClient();
+
// Called when user picked a value.
virtual void didChooseValue(const String&) = 0;
// Called when user picked a value.
virtual void didChooseValue(double) = 0;
// Called when chooser has ended.
virtual void didEndChooser() = 0;
-
- virtual void trace(Visitor*) { }
};
} // namespace blink
#if OS(WIN)
, m_paintTextFlags(0)
, m_minSizeForAntiAlias(0)
+ , m_minSizeForSubpixel(0)
, m_useSubpixelPositioning(false)
#endif
{
#if OS(WIN)
, m_paintTextFlags(0)
, m_minSizeForAntiAlias(0)
+ , m_minSizeForSubpixel(0)
, m_useSubpixelPositioning(false)
#endif
{
#if OS(WIN)
, m_paintTextFlags(0)
, m_minSizeForAntiAlias(0)
+ , m_minSizeForSubpixel(0)
, m_useSubpixelPositioning(false)
#endif
{
#if OS(WIN)
, m_paintTextFlags(src.m_paintTextFlags)
, m_minSizeForAntiAlias(src.m_minSizeForAntiAlias)
+ , m_minSizeForSubpixel(src.m_minSizeForSubpixel)
, m_useSubpixelPositioning(src.m_useSubpixelPositioning)
#endif
{
#if OS(WIN)
, m_paintTextFlags(0)
, m_minSizeForAntiAlias(0)
+ , m_minSizeForSubpixel(0)
, m_useSubpixelPositioning(subpixelTextPosition)
#endif
{
#if OS(WIN)
, m_paintTextFlags(src.m_paintTextFlags)
, m_minSizeForAntiAlias(src.m_minSizeForAntiAlias)
+ , m_minSizeForSubpixel(src.m_minSizeForSubpixel)
, m_useSubpixelPositioning(src.m_useSubpixelPositioning)
#endif
{
#if OS(WIN)
m_paintTextFlags = 0;
m_minSizeForAntiAlias = src.m_minSizeForAntiAlias;
+ m_minSizeForSubpixel = src.m_minSizeForSubpixel;
m_useSubpixelPositioning = src.m_useSubpixelPositioning;
#endif
#if OS(WIN)
void setMinSizeForAntiAlias(unsigned size) { m_minSizeForAntiAlias = size; }
unsigned minSizeForAntiAlias() const { return m_minSizeForAntiAlias; }
+ void setMinSizeForSubpixel(float size) { m_minSizeForSubpixel = size; }
+ float minSizeForSubpixel() const { return m_minSizeForSubpixel; }
void setHinting(SkPaint::Hinting style)
{
m_style.useAutoHint = 0;
int m_paintTextFlags;
bool m_useSubpixelPositioning;
unsigned m_minSizeForAntiAlias;
+ float m_minSizeForSubpixel;
#endif
};
m_fontMetrics.setLineGap(lineGap);
m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap));
- SkScalar underlineThickness, underlinePosition;
- if (metrics.hasUnderlineThickness(&underlineThickness)
- && metrics.hasUnderlinePosition(&underlinePosition)) {
- m_fontMetrics.setUnderlineThickness(SkScalarToFloat(underlineThickness));
- m_fontMetrics.setUnderlinePosition(SkScalarToFloat(-underlinePosition));
- }
-
if (platformData().orientation() == Vertical && !isTextOrientationFallback()) {
static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a');
static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G');
}
}
+ // List of fonts that look bad with subpixel text rendering at smaller font
+ // sizes. This includes all fonts in the Microsoft Core fonts for the Web
+ // collection.
+ const static wchar_t* noSubpixelForSmallSizeFont[] = {
+ L"andale mono",
+ L"arial",
+ L"comic sans",
+ L"courier new",
+ L"georgia",
+ L"impact",
+ L"lucida console",
+ L"tahoma",
+ L"times new roman",
+ L"trebuchet ms",
+ L"verdana",
+ L"webdings"
+ };
+ const static float minSizeForSubpixelForFont = 16.0f;
+ numFonts = WTF_ARRAY_LENGTH(noSubpixelForSmallSizeFont);
+ for (size_t i = 0; i < numFonts; i++) {
+ const wchar_t* family = noSubpixelForSmallSizeFont[i];
+ if (typefacesMatchesFamily(tf.get(), family)) {
+ result->setMinSizeForSubpixel(minSizeForSubpixelForFont);
+ break;
+ }
+ }
+
return result;
}
// run without font smoothing we enable it for tests to ensure we get
// good test coverage matching the more common smoothing enabled
// behavior.
- if (m_useSubpixelPositioning
+ if (m_useSubpixelPositioning && ts >= m_minSizeForSubpixel
&& ((textFlags & SkPaint::kAntiAlias_Flag) || LayoutTestSupport::isRunningLayoutTest()))
flags |= SkPaint::kSubpixelText_Flag;
PassRefPtr<Image> BitmapImage::imageForDefaultFrame()
{
- if (isBitmapImage() && maybeAnimated())
+ if (isAnimated())
return BitmapImage::create(frameAtIndex(0));
return Image::imageForDefaultFrame();
{
if (m_animationFinished)
return false;
- if (frameCount() > 1)
+ if (isAnimated())
return true;
+
return m_source.repetitionCount() != cAnimationNone;
}
+bool BitmapImage::isAnimated()
+{
+ return frameCount() > 1;
+}
+
void BitmapImage::advanceAnimation(Timer<BitmapImage>*)
{
internalAdvanceAnimation(false);
virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, WebBlendMode) OVERRIDE;
virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, WebBlendMode, RespectImageOrientationEnum) OVERRIDE;
+ // True if the image is animated (contains multiple frames)
+ bool isAnimated();
size_t currentFrame() const { return m_currentFrame; }
size_t frameCount();
PassRefPtr<NativeImageSkia> frameAtIndex(size_t);
}
// ImageBufferSurface implementation
- virtual void finalizeFrame() OVERRIDE { m_layerBridge->finalizeFrame(); }
+ virtual void finalizeFrame(const FloatRect &dirtyRect) OVERRIDE { m_layerBridge->finalizeFrame(dirtyRect); }
virtual void willAccessPixels() OVERRIDE { m_layerBridge->willAccessPixels(); }
virtual SkCanvas* canvas() const OVERRIDE { return m_layerBridge->canvas(); }
virtual bool isValid() const OVERRIDE { return m_layerBridge && m_layerBridge->checkSurfaceValid(); }
void Canvas2DLayerBridge::freeTransientResources()
{
ASSERT(!m_destructionInProgress);
+ if (!m_isSurfaceValid)
+ return;
freeReleasedMailbox();
flush();
freeMemoryIfPossible(bytesAllocated());
ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
ASSERT(mailboxInfo->m_image.get());
+
+ // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
+ // live mailboxes
+ ASSERT(!mailboxInfo->m_parentLayerBridge);
+ mailboxInfo->m_parentLayerBridge = this;
+ *outMailbox = mailboxInfo->m_mailbox;
+
+ GrContext* grContext = m_contextProvider->grContext();
+ if (!grContext)
+ return true; // for testing: skip gl stuff when using a mock graphics context.
+
ASSERT(mailboxInfo->m_image->getTexture());
// Because of texture sharing with the compositor, we must invalidate
webContext->bindTexture(GL_TEXTURE_2D, 0);
// Because we are changing the texture binding without going through skia,
// we must dirty the context.
- m_contextProvider->grContext()->resetContext(kTextureBinding_GrGLBackendState);
-
- // set m_parentLayerBridge to make sure 'this' stays alive as long as it has
- // live mailboxes
- ASSERT(!mailboxInfo->m_parentLayerBridge);
- mailboxInfo->m_parentLayerBridge = this;
- *outMailbox = mailboxInfo->m_mailbox;
+ grContext->resetContext(kTextureBinding_GrGLBackendState);
return true;
}
// texture and remove the mailbox from list to avoid reusing it
// in future.
if (mailboxInfo->m_image) {
- mailboxInfo->m_image->getTexture()->resetFlag(
- static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit));
- mailboxInfo->m_image->getTexture()->textureParamsModified();
+ GrTexture* texture = mailboxInfo->m_image->getTexture();
+ if (texture) {
+ texture->resetFlag(static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit));
+ texture->textureParamsModified();
+ }
mailboxInfo->m_image.clear();
}
- size_t i = mailboxInfo - m_mailboxes.begin();
- m_mailboxes.remove(i);
- Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
- // Here we need to return early since mailboxInfo removal would
- // also clear m_parentLayerBridge reference.
+ if (m_destructionInProgress) {
+ mailboxInfo->m_status = MailboxAvailable; // To satisfy assert in destructor
+
+ // The following line may trigger self destruction. We do not care about
+ // not cleaning up m_mailboxes during destruction sequence because
+ // mailboxes will not be recycled after this point. Calling remove()
+ // could trigger a memory use after free, so we just clear the self
+ // reference to be safe, and we let the Canvas2DLayerBridge destructor
+ // take care of freeing m_mailboxes.
+ mailboxInfo->m_parentLayerBridge.clear();
+ } else {
+ size_t i = mailboxInfo - m_mailboxes.begin();
+ m_mailboxes.remove(i); // indirectly clears mailboxInfo->m_parentLayerBridge
+ Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
+ }
+ // mailboxInfo is not valid from this point, so we return immediately.
return;
} else {
mailboxInfo->m_status = MailboxReleased;
return m_layer->layer();
}
-void Canvas2DLayerBridge::finalizeFrame()
+void Canvas2DLayerBridge::finalizeFrame(const FloatRect &dirtyRect)
{
ASSERT(!m_destructionInProgress);
Canvas2DLayerManager::get().layerDidDraw(this);
+ m_layer->layer()->invalidateRect(dirtyRect);
m_didRecordDrawCommand = true;
}
Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) {
// This copy constructor should only be used for Vector reallocation
- // Assuming 'other' is to be destroyed, we transfer m_image ownership
- // rather than do a refcount dance.
+ // Assuming 'other' is to be destroyed, we transfer m_image and
+ // m_parentLayerBridge ownership rather than do a refcount dance.
memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox));
m_image = const_cast<MailboxInfo*>(&other)->m_image.release();
+ m_parentLayerBridge = const_cast<MailboxInfo*>(&other)->m_parentLayerBridge.release();
m_status = other.m_status;
}
virtual void skippedPendingDrawCommands() OVERRIDE;
// ImageBufferSurface implementation
- void finalizeFrame();
+ void finalizeFrame(const FloatRect &dirtyRect);
void willAccessPixels();
SkCanvas* canvas() const { return m_canvas.get(); }
bool checkSurfaceValid();
::testing::Mock::VerifyAndClearExpectations(&mainMock);
}
+ void noDrawOnContextLostTest()
+ {
+ MockCanvasContext mainMock;
+ OwnPtr<MockWebGraphicsContext3DProvider> mainMockProvider = adoptPtr(new MockWebGraphicsContext3DProvider(&mainMock));
+ RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterPMColor(300, 150));
+ OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
+
+ ::testing::Mock::VerifyAndClearExpectations(&mainMock);
+
+ {
+ Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(mainMockProvider.release(), canvas.release(), surface, 0, NonOpaque)));
+ ::testing::Mock::VerifyAndClearExpectations(&mainMock);
+ EXPECT_TRUE(bridge->checkSurfaceValid());
+ SkPaint paint;
+ uint32_t genID = surface->generationID();
+ bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
+ EXPECT_EQ(genID, surface->generationID());
+ mainMock.fakeContextLost();
+ EXPECT_EQ(genID, surface->generationID());
+ bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
+ EXPECT_EQ(genID, surface->generationID());
+ EXPECT_FALSE(bridge->checkSurfaceValid());
+ EXPECT_EQ(genID, surface->generationID());
+ bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
+ EXPECT_EQ(genID, surface->generationID());
+ bridge->freeTransientResources();
+ EXPECT_EQ(genID, surface->generationID());
+ ::testing::Mock::VerifyAndClearExpectations(&mainMock);
+ }
+
+ ::testing::Mock::VerifyAndClearExpectations(&mainMock);
+ }
+
void prepareMailboxWithBitmapTest()
{
MockCanvasContext mainMock;
bridge->prepareMailbox(0, &bitmap);
EXPECT_EQ(0u, bridge->m_lastImageId);
}
+
+ void prepareMailboxAndLoseResourceTest()
+ {
+ MockCanvasContext mainMock;
+ RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterPMColor(300, 150));
+ bool lostResource = true;
+
+ // Prepare a mailbox, then report the resource as lost.
+ // This test passes by not crashing and not triggering assertions.
+ {
+ WebExternalTextureMailbox mailbox;
+ OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
+ OwnPtr<MockWebGraphicsContext3DProvider> mainMockProvider = adoptPtr(new MockWebGraphicsContext3DProvider(&mainMock));
+ Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(mainMockProvider.release(), canvas.release(), surface, 0, NonOpaque)));
+ bridge->prepareMailbox(&mailbox, 0);
+ bridge->mailboxReleased(mailbox, lostResource);
+ }
+
+ // Retry with mailbox released while bridge destruction is in progress
+ {
+ OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
+ OwnPtr<MockWebGraphicsContext3DProvider> mainMockProvider = adoptPtr(new MockWebGraphicsContext3DProvider(&mainMock));
+ WebExternalTextureMailbox mailbox;
+ Canvas2DLayerBridge* rawBridge;
+ {
+ Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(mainMockProvider.release(), canvas.release(), surface, 0, NonOpaque)));
+ bridge->prepareMailbox(&mailbox, 0);
+ rawBridge = bridge.get();
+ } // bridge goes out of scope, but object is kept alive by self references
+ // before fixing crbug.com/411864, the following line you cause a memory use after free
+ // that sometimes causes a crash in normal builds and crashes consistently with ASAN.
+ rawBridge->mailboxReleased(mailbox, lostResource); // This should self-destruct the bridge.
+ }
+ }
};
namespace {
fullLifecycleTest();
}
-TEST_F(Canvas2DLayerBridgeTest, prepareMailboxWithBitmapTest)
+TEST_F(Canvas2DLayerBridgeTest, testNoDrawOnContextLost)
+{
+ noDrawOnContextLostTest();
+}
+
+TEST_F(Canvas2DLayerBridgeTest, testPrepareMailboxWithBitmap)
{
prepareMailboxWithBitmapTest();
}
+TEST_F(Canvas2DLayerBridgeTest, testPrepareMailboxAndLoseResource)
+{
+ prepareMailboxAndLoseResourceTest();
+}
+
} // namespace
void doDeferredFrameTestTask(FakeCanvas2DLayerBridge* layer, bool skipCommands)
{
+ FloatRect invalidationRect(0, 0, 1, 1);
EXPECT_FALSE(Canvas2DLayerManager::get().m_taskObserverActive);
- layer->finalizeFrame();
+ layer->finalizeFrame(invalidationRect);
layer->storageAllocatedForRecordingChanged(1);
EXPECT_TRUE(Canvas2DLayerManager::get().m_taskObserverActive);
if (skipCommands) {
- layer->finalizeFrame();
+ layer->finalizeFrame(invalidationRect);
layer->skippedPendingDrawCommands();
}
Platform::current()->currentThread()->exitRunLoop();
{ CompositingReasonSquashingTransformAncestorMismatch,
"squashingTransformAncestorMismatch",
"Cannot be squashed because this layer has a different transform ancestor than the squashing layer" },
- { CompositingReasonSquashingFilterAncestorMismatch,
+ { CompositingReasonSquashingFilterMismatch,
"squashingFilterAncestorMismatch",
- "Cannot be squashed because this layer has a different filter ancestor than the squashing layer" },
+ "Cannot be squashed because this layer has a different filter ancestor than the squashing layer, or this layer has a filter" },
{ CompositingReasonSquashingWouldBreakPaintOrder,
"squashingWouldBreakPaintOrder",
"Cannot be squashed without breaking paint order" },
const uint64_t CompositingReasonSquashingClippingContainerMismatch = UINT64_C(1) << 19;
const uint64_t CompositingReasonSquashingOpacityAncestorMismatch = UINT64_C(1) << 20;
const uint64_t CompositingReasonSquashingTransformAncestorMismatch = UINT64_C(1) << 21;
-const uint64_t CompositingReasonSquashingFilterAncestorMismatch = UINT64_C(1) << 22;
+const uint64_t CompositingReasonSquashingFilterMismatch = UINT64_C(1) << 22;
const uint64_t CompositingReasonSquashingWouldBreakPaintOrder = UINT64_C(1) << 23;
const uint64_t CompositingReasonSquashingVideoIsDisallowed = UINT64_C(1) << 24;
const uint64_t CompositingReasonSquashedLayerClipsCompositingDescendants = UINT64_C(1) << 25;
| CompositingReasonSquashingClippingContainerMismatch
| CompositingReasonSquashingOpacityAncestorMismatch
| CompositingReasonSquashingTransformAncestorMismatch
- | CompositingReasonSquashingFilterAncestorMismatch
+ | CompositingReasonSquashingFilterMismatch
| CompositingReasonSquashingWouldBreakPaintOrder
| CompositingReasonSquashingVideoIsDisallowed
| CompositingReasonSquashedLayerClipsCompositingDescendants
m_contentsOpaque = opaque;
m_layer->layer()->setOpaque(m_contentsOpaque);
m_opaqueRectTrackingContentLayerDelegate->setOpaque(m_contentsOpaque);
+ clearContentsLayerIfUnregistered();
if (m_contentsLayer)
m_contentsLayer->setOpaque(opaque);
}
m_client->didFinalizeFrame();
}
-void ImageBuffer::finalizeFrame()
+void ImageBuffer::finalizeFrame(const FloatRect &dirtyRect)
{
- m_surface->finalizeFrame();
+ m_surface->finalizeFrame(dirtyRect);
didFinalizeFrame();
}
GraphicsContext* context() const;
// Called at the end of a task that rendered a whole frame
- void finalizeFrame();
+ void finalizeFrame(const FloatRect &dirtyRect);
void didFinalizeFrame();
bool isDirty();
class ImageBuffer;
class WebLayer;
+class FloatRect;
enum OpacityMode {
NonOpaque,
virtual void setImageBuffer(ImageBuffer*) { }
virtual PassRefPtr<SkPicture> getPicture();
virtual void didClearCanvas() { }
- virtual void finalizeFrame() { }
+ virtual void finalizeFrame(const FloatRect &dirtyRect) { }
OpacityMode opacityMode() const { return m_opacityMode; }
const IntSize& size() const { return m_size; }
return nullptr;
}
-void RecordingImageBufferSurface::finalizeFrame()
+void RecordingImageBufferSurface::finalizeFrame(const FloatRect &)
{
if (!finalizeFrameInternal() && !m_rasterCanvas) {
fallBackToRasterCanvas();
virtual PassRefPtr<SkPicture> getPicture() OVERRIDE;
virtual bool isValid() const OVERRIDE { return true; }
virtual void willAccessPixels() OVERRIDE;
- virtual void finalizeFrame() OVERRIDE;
+ virtual void finalizeFrame(const FloatRect&) OVERRIDE;
virtual void didClearCanvas() OVERRIDE;
virtual void setImageBuffer(ImageBuffer*) OVERRIDE;
virtual void didProcessTask() OVERRIDE
{
ASSERT_TRUE(m_isDirty);
- m_imageBuffer->finalizeFrame();
+ FloatRect dirtyRect(0, 0, 1, 1);
+ m_imageBuffer->finalizeFrame(dirtyRect);
ASSERT_FALSE(m_isDirty);
}
public:
MockWebGraphicsContext3D()
: m_nextTextureId(1)
+ , m_contextLost(false)
{
}
virtual void synthesizeGLError(WGC3Denum) { }
- virtual bool isContextLost() { return false; }
+ virtual bool isContextLost() { return m_contextLost; }
virtual void* mapBufferSubDataCHROMIUM(WGC3Denum target, WGC3Dintptr offset, WGC3Dsizeiptr size, WGC3Denum access) { return 0; }
virtual void unmapBufferSubDataCHROMIUM(const void*) { }
virtual WebString getTranslatedShaderSourceANGLE(WebGLId) { return WebString(); }
+ void fakeContextLost() { m_contextLost = true; }
protected:
unsigned m_nextTextureId;
+ bool m_contextLost;
Attributes m_attrs;
};
~DummyBase() { }
};
-// Export this instance to support WillBeGarbageCollectedMixin
-// uses by code residing in non-webcore components.
-template class PLATFORM_EXPORT DummyBase<void>;
-
#define PassRefPtrWillBeRawPtr WTF::PassRefPtr
#define RefCountedWillBeGarbageCollected WTF::RefCounted
#define RefCountedWillBeGarbageCollectedFinalized WTF::RefCounted
return controller.release();
}
-PassRefPtrWillBeRawPtr<DateTimeChooser> ChromeClientImpl::openDateTimeChooser(DateTimeChooserClient* pickerClient, const DateTimeChooserParameters& parameters)
+PassRefPtr<DateTimeChooser> ChromeClientImpl::openDateTimeChooser(DateTimeChooserClient* pickerClient, const DateTimeChooserParameters& parameters)
{
#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
return DateTimeChooserImpl::create(this, pickerClient, parameters);
virtual void annotatedRegionsChanged() OVERRIDE;
virtual bool paintCustomOverhangArea(GraphicsContext*, const IntRect&, const IntRect&, const IntRect&) OVERRIDE;
virtual PassOwnPtr<ColorChooser> createColorChooser(LocalFrame*, ColorChooserClient*, const Color&) OVERRIDE;
- virtual PassRefPtrWillBeRawPtr<DateTimeChooser> openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&) OVERRIDE;
+ virtual PassRefPtr<DateTimeChooser> openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&) OVERRIDE;
virtual void openTextDataListChooser(HTMLInputElement&) OVERRIDE;
virtual void runOpenPanel(LocalFrame*, PassRefPtr<FileChooser>) OVERRIDE;
virtual void enumerateChosenDirectory(FileChooser*) OVERRIDE;
m_popup = m_chromeClient->openPagePopup(this, m_parameters.anchorRectInRootView);
}
-PassRefPtrWillBeRawPtr<DateTimeChooserImpl> DateTimeChooserImpl::create(ChromeClientImpl* chromeClient, DateTimeChooserClient* client, const DateTimeChooserParameters& parameters)
+PassRefPtr<DateTimeChooserImpl> DateTimeChooserImpl::create(ChromeClientImpl* chromeClient, DateTimeChooserClient* client, const DateTimeChooserParameters& parameters)
{
- return adoptRefWillBeNoop(new DateTimeChooserImpl(chromeClient, client, parameters));
+ return adoptRef(new DateTimeChooserImpl(chromeClient, client, parameters));
}
DateTimeChooserImpl::~DateTimeChooserImpl()
void DateTimeChooserImpl::setValueAndClosePopup(int numValue, const String& stringValue)
{
- RefPtrWillBeRawPtr<DateTimeChooserImpl> protector(this);
+ RefPtr<DateTimeChooserImpl> protector(this);
if (numValue >= 0)
setValue(stringValue);
endChooser();
m_client->didEndChooser();
}
-void DateTimeChooserImpl::trace(Visitor* visitor)
-{
- visitor->trace(m_client);
- DateTimeChooser::trace(visitor);
-}
-
} // namespace blink
#endif // ENABLE(INPUT_MULTIPLE_FIELDS_UI)
class DateTimeChooserImpl FINAL : public DateTimeChooser, public PagePopupClient {
public:
- static PassRefPtrWillBeRawPtr<DateTimeChooserImpl> create(ChromeClientImpl*, DateTimeChooserClient*, const DateTimeChooserParameters&);
+ static PassRefPtr<DateTimeChooserImpl> create(ChromeClientImpl*, DateTimeChooserClient*, const DateTimeChooserParameters&);
virtual ~DateTimeChooserImpl();
- virtual void trace(Visitor*) OVERRIDE;
// DateTimeChooser functions:
virtual void endChooser() OVERRIDE;
virtual void didClosePopup() OVERRIDE;
ChromeClientImpl* m_chromeClient;
- RawPtrWillBeMember<DateTimeChooserClient> m_client;
+ DateTimeChooserClient* m_client;
PagePopup* m_popup;
DateTimeChooserParameters m_parameters;
OwnPtr<Locale> m_locale;
ASSERT(client);
}
-PassRefPtrWillBeRawPtr<ExternalDateTimeChooser> ExternalDateTimeChooser::create(ChromeClientImpl* chromeClient, WebViewClient* webViewClient, DateTimeChooserClient* client, const DateTimeChooserParameters& parameters)
+PassRefPtr<ExternalDateTimeChooser> ExternalDateTimeChooser::create(ChromeClientImpl* chromeClient, WebViewClient* webViewClient, DateTimeChooserClient* client, const DateTimeChooserParameters& parameters)
{
ASSERT(chromeClient);
- RefPtrWillBeRawPtr<ExternalDateTimeChooser> chooser = adoptRefWillBeNoop(new ExternalDateTimeChooser(client));
+ RefPtr<ExternalDateTimeChooser> chooser = adoptRef(new ExternalDateTimeChooser(client));
if (!chooser->openDateTimeChooser(chromeClient, webViewClient, parameters))
chooser.clear();
return chooser.release();
class ExternalDateTimeChooser FINAL : public blink::DateTimeChooser {
public:
- static PassRefPtrWillBeRawPtr<ExternalDateTimeChooser> create(ChromeClientImpl*, WebViewClient*, blink::DateTimeChooserClient*, const blink::DateTimeChooserParameters&);
+ static PassRefPtr<ExternalDateTimeChooser> create(ChromeClientImpl*, WebViewClient*, blink::DateTimeChooserClient*, const blink::DateTimeChooserParameters&);
virtual ~ExternalDateTimeChooser();
// The following functions are for DateTimeChooserCompletion.
}
WebPopupMenuInfo info;
- getPopupMenuInfo(&info);
+ getPopupMenuInfo(info, *m_popupMenuClient);
if (info.items.isEmpty())
return;
m_webExternalPopupMenu = m_webView.client()->createExternalPopupMenu(info, this);
void ExternalPopupMenu::didChangeSelection(int index)
{
if (m_popupMenuClient)
- m_popupMenuClient->selectionChanged(toPopupMenuItemIndex(index));
+ m_popupMenuClient->selectionChanged(toPopupMenuItemIndex(index, *m_popupMenuClient));
}
void ExternalPopupMenu::didAcceptIndex(int index)
// Calling methods on the PopupMenuClient might lead to this object being
// derefed. This ensures it does not get deleted while we are running this
// method.
- int popupMenuItemIndex = toPopupMenuItemIndex(index);
+ int popupMenuItemIndex = toPopupMenuItemIndex(index, *m_popupMenuClient);
RefPtr<ExternalPopupMenu> guard(this);
if (m_popupMenuClient) {
m_popupMenuClient->valueChanged(static_cast<unsigned>(-1), true);
else {
for (size_t i = 0; i < indices.size(); ++i)
- m_popupMenuClient->listBoxSelectItem(toPopupMenuItemIndex(indices[i]), (i > 0), false, (i == indices.size() - 1));
+ m_popupMenuClient->listBoxSelectItem(toPopupMenuItemIndex(indices[i], *m_popupMenuClient), (i > 0), false, (i == indices.size() - 1));
}
// The call to valueChanged above might have lead to a call to
m_webExternalPopupMenu = 0;
}
-void ExternalPopupMenu::getPopupMenuInfo(WebPopupMenuInfo* info)
+void ExternalPopupMenu::getPopupMenuInfo(WebPopupMenuInfo& info, PopupMenuClient& popupMenuClient)
{
- int itemCount = m_popupMenuClient->listSize();
+ int itemCount = popupMenuClient.listSize();
int count = 0;
Vector<WebMenuItemInfo> items(static_cast<size_t>(itemCount));
for (int i = 0; i < itemCount; ++i) {
- PopupMenuStyle style = m_popupMenuClient->itemStyle(i);
+ PopupMenuStyle style = popupMenuClient.itemStyle(i);
if (style.isDisplayNone())
continue;
WebMenuItemInfo& popupItem = items[count++];
- popupItem.label = m_popupMenuClient->itemText(i);
- popupItem.toolTip = m_popupMenuClient->itemToolTip(i);
- if (m_popupMenuClient->itemIsSeparator(i))
+ popupItem.label = popupMenuClient.itemText(i);
+ popupItem.toolTip = popupMenuClient.itemToolTip(i);
+ if (popupMenuClient.itemIsSeparator(i))
popupItem.type = WebMenuItemInfo::Separator;
- else if (m_popupMenuClient->itemIsLabel(i))
+ else if (popupMenuClient.itemIsLabel(i))
popupItem.type = WebMenuItemInfo::Group;
else
popupItem.type = WebMenuItemInfo::Option;
- popupItem.enabled = m_popupMenuClient->itemIsEnabled(i);
- popupItem.checked = m_popupMenuClient->itemIsSelected(i);
+ popupItem.enabled = popupMenuClient.itemIsEnabled(i);
+ popupItem.checked = popupMenuClient.itemIsSelected(i);
popupItem.textDirection = toWebTextDirection(style.textDirection());
popupItem.hasTextDirectionOverride = style.hasTextDirectionOverride();
}
- info->itemHeight = m_popupMenuClient->menuStyle().font().fontMetrics().height();
- info->itemFontSize = static_cast<int>(m_popupMenuClient->menuStyle().font().fontDescription().computedSize());
- info->selectedIndex = toExternalPopupMenuItemIndex(m_popupMenuClient->selectedIndex());
- info->rightAligned = m_popupMenuClient->menuStyle().textDirection() == RTL;
- info->allowMultipleSelection = m_popupMenuClient->multiple();
- info->items = items;
+ info.itemHeight = popupMenuClient.menuStyle().font().fontMetrics().height();
+ info.itemFontSize = static_cast<int>(popupMenuClient.menuStyle().font().fontDescription().computedSize());
+ info.selectedIndex = toExternalPopupMenuItemIndex(popupMenuClient.selectedIndex(), popupMenuClient);
+ info.rightAligned = popupMenuClient.menuStyle().textDirection() == RTL;
+ info.allowMultipleSelection = popupMenuClient.multiple();
+ if (count < itemCount)
+ items.shrink(count);
+ info.items = items;
+
}
-int ExternalPopupMenu::toPopupMenuItemIndex(int externalPopupMenuItemIndex)
+int ExternalPopupMenu::toPopupMenuItemIndex(int externalPopupMenuItemIndex, PopupMenuClient& popupMenuClient)
{
- ASSERT(m_popupMenuClient);
if (externalPopupMenuItemIndex < 0)
return externalPopupMenuItemIndex;
- int itemCount = m_popupMenuClient->listSize();
+ int itemCount = popupMenuClient.listSize();
int indexTracker = 0;
for (int i = 0; i < itemCount ; ++i) {
- if (m_popupMenuClient->itemStyle(i).isDisplayNone())
+ if (popupMenuClient.itemStyle(i).isDisplayNone())
continue;
if (indexTracker++ == externalPopupMenuItemIndex)
return i;
return -1;
}
-int ExternalPopupMenu::toExternalPopupMenuItemIndex(int popupMenuItemIndex)
+int ExternalPopupMenu::toExternalPopupMenuItemIndex(int popupMenuItemIndex, PopupMenuClient& popupMenuClient)
{
- ASSERT(m_popupMenuClient);
if (popupMenuItemIndex < 0)
return popupMenuItemIndex;
- int itemCount = m_popupMenuClient->listSize();
+ int itemCount = popupMenuClient.listSize();
int indexTracker = 0;
for (int i = 0; i < itemCount; ++i) {
- if (m_popupMenuClient->itemStyle(i).isDisplayNone())
+ if (popupMenuClient.itemStyle(i).isDisplayNone())
continue;
if (popupMenuItemIndex == i)
return indexTracker;
ExternalPopupMenu(LocalFrame&, PopupMenuClient*, WebViewImpl&);
virtual ~ExternalPopupMenu();
+
+ // Fills |info| with the popup menu information contained in the
+ // PopupMenuClient associated with this ExternalPopupMenu.
+ // FIXME: public only for test access. Need to revert once gtest
+ // helpers from chromium are available for blink.
+ static void getPopupMenuInfo(WebPopupMenuInfo&, PopupMenuClient&);
+ static int toPopupMenuItemIndex(int index, PopupMenuClient&);
+ static int toExternalPopupMenuItemIndex(int index, PopupMenuClient&);
+
private:
// PopupMenu methods:
virtual void show(const FloatQuad& controlPosition, const IntSize&, int index) OVERRIDE;
virtual void didCancel() OVERRIDE;
void dispatchEvent(Timer<ExternalPopupMenu>*);
- // Fills |info| with the popup menu information contained in the
- // PopupMenuClient associated with this ExternalPopupMenu.
- void getPopupMenuInfo(WebPopupMenuInfo* info);
- int toPopupMenuItemIndex(int index);
- int toExternalPopupMenuItemIndex(int index);
PopupMenuClient* m_popupMenuClient;
RefPtr<FrameView> m_frameView;
--- /dev/null
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "web/ExternalPopupMenu.h"
+
+#include "platform/PopupMenu.h"
+#include "platform/PopupMenuClient.h"
+#include "public/web/WebPopupMenuInfo.h"
+#include <gtest/gtest.h>
+
+using namespace blink;
+
+namespace {
+
+const size_t kListSize = 7;
+
+class TestPopupMenuClient : public PopupMenuClient {
+public:
+ TestPopupMenuClient() : m_listSize(0) { }
+ virtual ~TestPopupMenuClient() { }
+
+ virtual void valueChanged(unsigned listIndex, bool fireEvents = true) OVERRIDE { }
+ virtual void selectionChanged(unsigned listIndex, bool fireEvents = true) OVERRIDE { }
+ virtual void selectionCleared() OVERRIDE { }
+
+ virtual String itemText(unsigned listIndex) const OVERRIDE { return emptyString(); }
+ virtual String itemToolTip(unsigned listIndex) const OVERRIDE { return emptyString(); }
+ virtual String itemAccessibilityText(unsigned listIndex) const OVERRIDE { return emptyString(); }
+ virtual bool itemIsEnabled(unsigned listIndex) const OVERRIDE { return true; }
+ virtual PopupMenuStyle itemStyle(unsigned listIndex) const OVERRIDE
+ {
+ FontDescription fontDescription;
+ fontDescription.setComputedSize(12.0);
+ Font font(fontDescription);
+ font.update(nullptr);
+ bool displayNone = m_displayNoneIndexSet.find(listIndex) != m_displayNoneIndexSet.end();
+ return PopupMenuStyle(Color::black, Color::white, font, true, displayNone, Length(), TextDirection(), false);
+ }
+ virtual PopupMenuStyle menuStyle() const OVERRIDE { return itemStyle(0); }
+ virtual LayoutUnit clientPaddingLeft() const OVERRIDE { return 0; }
+ virtual LayoutUnit clientPaddingRight() const OVERRIDE { return 0; }
+ virtual int listSize() const OVERRIDE { return m_listSize; }
+ virtual int selectedIndex() const OVERRIDE { return 0; }
+ virtual void popupDidHide() OVERRIDE { }
+ virtual bool itemIsSeparator(unsigned listIndex) const OVERRIDE { return false;}
+ virtual bool itemIsLabel(unsigned listIndex) const OVERRIDE { return false; }
+ virtual bool itemIsSelected(unsigned listIndex) const OVERRIDE { return listIndex == 0;}
+ virtual void setTextFromItem(unsigned listIndex) OVERRIDE { }
+ virtual bool multiple() const OVERRIDE { return false; }
+
+ void setListSize(size_t size) { m_listSize = size; }
+ void setDisplayNoneIndex(unsigned index) { m_displayNoneIndexSet.insert(index); }
+private:
+ size_t m_listSize;
+ std::set<unsigned> m_displayNoneIndexSet;
+};
+
+class ExternalPopupMenuDisplayNoneItemsTest : public testing::Test {
+public:
+ ExternalPopupMenuDisplayNoneItemsTest() { }
+
+protected:
+ virtual void SetUp() OVERRIDE
+ {
+ m_popupMenuClient.setListSize(kListSize);
+
+ // Set the 4th an 5th items to have "display: none" property
+ m_popupMenuClient.setDisplayNoneIndex(3);
+ m_popupMenuClient.setDisplayNoneIndex(4);
+ }
+
+ TestPopupMenuClient m_popupMenuClient;
+};
+
+TEST_F(ExternalPopupMenuDisplayNoneItemsTest, PopupMenuInfoSizeTest)
+{
+ WebPopupMenuInfo info;
+ ExternalPopupMenu::getPopupMenuInfo(info, m_popupMenuClient);
+ EXPECT_EQ(5U, info.items.size());
+}
+
+TEST_F(ExternalPopupMenuDisplayNoneItemsTest, IndexMappingTest)
+{
+ // 6th indexed item in popupmenu would be the 4th item in ExternalPopupMenu,
+ // and vice-versa.
+ EXPECT_EQ(4, ExternalPopupMenu::toExternalPopupMenuItemIndex(6, m_popupMenuClient));
+ EXPECT_EQ(6, ExternalPopupMenu::toPopupMenuItemIndex(4, m_popupMenuClient));
+
+ // Invalid index, methods should return -1.
+ EXPECT_EQ(-1, ExternalPopupMenu::toExternalPopupMenuItemIndex(8, m_popupMenuClient));
+ EXPECT_EQ(-1, ExternalPopupMenu::toPopupMenuItemIndex(8, m_popupMenuClient));
+}
+
+} // namespace
Position startPosition = startVisiblePosition.deepEquivalent();
Position endPosition = endVisiblePosition.deepEquivalent();
+ // document() will return null if -webkit-user-select is set to none.
+ if (!startPosition.document() || !endPosition.document())
+ return;
+
RefPtrWillBeRawPtr<Range> range = Range::create(*startPosition.document(), startPosition, endPosition);
if (!range)
return;
EXPECT_STREQ(kExpectedClipHtml, clipHtml.utf8().c_str());
}
+TEST_F(WebViewTest, SmartClipReturnsEmptyStringsWhenUserSelectIsNone)
+{
+ WebString clipText;
+ WebString clipHtml;
+ WebRect clipRect;
+ URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("Ahem.ttf"));
+ URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("smartclip_user_select_none.html"));
+ WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "smartclip_user_select_none.html");
+ webView->setPageScaleFactorLimits(1, 1);
+ webView->resize(WebSize(500, 500));
+ webView->layout();
+ WebRect cropRect(0, 0, 100, 100);
+ webView->extractSmartClipData(cropRect, clipText, clipHtml, clipRect);
+ EXPECT_STREQ("", clipText.utf8().c_str());
+ EXPECT_STREQ("", clipHtml.utf8().c_str());
+}
+
class CreateChildCounterFrameClient : public FrameTestHelpers::TestWebFrameClient {
public:
CreateChildCounterFrameClient() : m_count(0) { }
--- /dev/null
+<!DOCTYPE html>
+<html>
+
+<head>
+ <style>
+ body {
+ -webkit-user-select:none;
+ }
+ </style>
+</head>
+
+<body>
+abcdefghijklmnopqrstuvwxyz<br>
+abcdefghijklmnopqrstuvwxyz<br>
+abcdefghijklmnopqrstuvwxyz<br>
+abcdefghijklmnopqrstuvwxyz<br>
+abcdefghijklmnopqrstuvwxyz<br>
+abcdefghijklmnopqrstuvwxyz<br>
+</body>
+
+</html>
'win/WebFontRendering.cpp',
],
'web_unittest_files': [
+ 'ExternalPopupMenuTest.cpp',
'WebNodeTest.cpp',
# FIXME: Move the tests from web/tests/ to appropriate places.
# crbug.com/353585
http://www.chromium.org/chromium-os/developer-guide/gerrit-guide
+You also need to add an ssh key at
+https://gerrit.chromium.org/gerrit/#/settings/ssh-keys . Generate a key
+following the GitHub guide linked from there, then copy the contents of your
+~/.ssh/id_rsa.pub file to that gerrit page.
+
If your change adds new files to the repository, you'll need to regenerate the
GYP defines by following the directions in chromium/scripts/generate_gyp.py.
'-Wno-pointer-sign',
# ffmpeg doesn't believe in exhaustive switch statements.
'-Wno-switch',
+ # matroskadec.c has a "failed:" label that's only used if some
+ # CONFIG_ flags we don't set are set.
+ '-Wno-unused-label',
],
},
'cflags': [
4116, 4307, 4273, 4005, 4056, 4756,
],
'conditions': [
- ['clang == 1 or (OS == "win" and (MSVS_VERSION == "2013" or MSVS_VERSION == "2013e"))', {
+ ['clang == 1', {
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ # This corresponds to msvs_disabled_warnings 4273 above.
+ 'AdditionalOptions': [ '-Wno-inconsistent-dllimport' ],
+ },
+ },
+ }],
+ ['clang == 1 or (MSVS_VERSION == "2013" or MSVS_VERSION == "2013e")', {
'defines': [
'inline=__inline',
'strtoll=_strtoi64',
int size;
union {
uint64_t u64;
- uint8_t u8[8];
+ uint8_t u8[8 + FF_INPUT_BUFFER_PADDING_SIZE];
} tmp;
tmp.u64 = av_be2ne64(state);
int err;
union {
uint64_t u64;
- uint8_t u8[8];
+ uint8_t u8[8 + FF_INPUT_BUFFER_PADDING_SIZE];
} tmp = { av_be2ne64(state) };
AC3HeaderInfo hdr, *phdr = &hdr;
GetBitContext gbc;
* A partial copy of libjpeg-turbo 1.3.1 (r1219);
* Revision r1188 cherry-picked from upstream trunk into config.h;
* Revision r1220 cherry-picked from upstream trunk into jchuff.c;
+* Revisions r1108, r1109, r1333, r1375, r1386, r1389 and r1390 cherry-picked
+ from upstream trunk for Arm64;
* A build file (libjpeg.gyp), and;
* Patched header files used by Chromium.
* Added the 'private_extern' flags on Mac (or the 'hidden' flags on Linux) to
all the global symbols in '.asm' files to prevent making them external ones.
* Supported motion-JPEG frames that do not have DHT markers.
+* Removed .func / .endfunc lines from arm assembly
+ ( https://sourceforge.net/p/libjpeg-turbo/bugs/72/ , landed at
+ https://sourceforge.net/p/libjpeg-turbo/code/1375 ).
The 'google.patch' file represents our changes from the original
libjpeg-turbo-1.2.
EXTN(jsimd_h2v2_downsample_mmx):
push ebp
+Index: simd/jsimd_arm_neon.S
+===================================================================
+--- simd/jsimd_arm_neon.S (revision 272637)
++++ simd/jsimd_arm_neon.S (working copy)
+@@ -41,11 +41,9 @@
+ /* Supplementary macro for setting function attributes */
+ .macro asm_function fname
+ #ifdef __APPLE__
+- .func _\fname
+ .globl _\fname
+ _\fname:
+ #else
+- .func \fname
+ .global \fname
+ #ifdef __ELF__
+ .hidden \fname
+@@ -670,7 +668,6 @@
+ .unreq ROW6R
+ .unreq ROW7L
+ .unreq ROW7R
+-.endfunc
+
+
+ /*****************************************************************************/
+@@ -895,7 +892,6 @@
+ .unreq TMP2
+ .unreq TMP3
+ .unreq TMP4
+-.endfunc
+
+
+ /*****************************************************************************/
+@@ -1108,7 +1104,6 @@
+ .unreq TMP2
+ .unreq TMP3
+ .unreq TMP4
+-.endfunc
+
+ .purgem idct_helper
+
+@@ -1263,7 +1258,6 @@
+ .unreq OUTPUT_COL
+ .unreq TMP1
+ .unreq TMP2
+-.endfunc
+
+ .purgem idct_helper
+
+@@ -1547,7 +1541,6 @@
+ .unreq U
+ .unreq V
+ .unreq N
+-.endfunc
+
+ .purgem do_yuv_to_rgb
+ .purgem do_yuv_to_rgb_stage1
+@@ -1858,7 +1851,6 @@
+ .unreq U
+ .unreq V
+ .unreq N
+-.endfunc
+
+ .purgem do_rgb_to_yuv
+ .purgem do_rgb_to_yuv_stage1
+@@ -1940,7 +1932,6 @@
+ .unreq TMP2
+ .unreq TMP3
+ .unreq TMP4
+-.endfunc
+
+
+ /*****************************************************************************/
+@@ -2064,7 +2055,6 @@
+
+ .unreq DATA
+ .unreq TMP
+-.endfunc
+
+
+ /*****************************************************************************/
+@@ -2166,7 +2156,6 @@
+ .unreq CORRECTION
+ .unreq SHIFT
+ .unreq LOOP_COUNT
+-.endfunc
+
+
+ /*****************************************************************************/
+@@ -2401,7 +2390,6 @@
+ .unreq WIDTH
+ .unreq TMP
+
+-.endfunc
+
+ .purgem upsample16
+ .purgem upsample32
Index: simd/jsimd_i386.c
===================================================================
--- simd/jsimd_i386.c (revision 829)
/* if run length > 15, must emit special run-length-16 codes (0xF0) */ \
while (r > 15) { \
EMIT_BITS(code_0xf0, size_0xf0) \
+Index: simd/jsimd_arm64.c
+===================================================================
+--- /dev/null
++++ simd/jsimd_arm64.c
+@@ -0,0 +1,544 @@
++/*
++ * jsimd_arm64.c
++ *
++ * Copyright 2009 Pierre Ossman <ossman@cendio.se> for Cendio AB
++ * Copyright 2009-2011, 2013-2014 D. R. Commander
++ *
++ * Based on the x86 SIMD extension for IJG JPEG library,
++ * Copyright (C) 1999-2006, MIYASAKA Masaru.
++ * For conditions of distribution and use, see copyright notice in jsimdext.inc
++ *
++ * This file contains the interface between the "normal" portions
++ * of the library and the SIMD implementations when running on a
++ * 64-bit ARM architecture.
++ */
++
++#define JPEG_INTERNALS
++#include "../jinclude.h"
++#include "../jpeglib.h"
++#include "../jsimd.h"
++#include "../jdct.h"
++#include "../jsimddct.h"
++#include "jsimd.h"
++
++#include <stdio.h>
++#include <string.h>
++#include <ctype.h>
++
++static unsigned int simd_support = ~0;
++
++/*
++ * Check what SIMD accelerations are supported.
++ *
++ * FIXME: This code is racy under a multi-threaded environment.
++ */
++
++/*
++ * ARMv8 architectures support NEON extensions by default.
++ * It is no longer optional as it was with ARMv7.
++ */
++
++
++LOCAL(void)
++init_simd (void)
++{
++ char *env = NULL;
++
++ if (simd_support != ~0U)
++ return;
++
++ simd_support = 0;
++
++ simd_support |= JSIMD_ARM_NEON;
++
++ /* Force different settings through environment variables */
++ env = getenv("JSIMD_FORCENEON");
++ if ((env != NULL) && (strcmp(env, "1") == 0))
++ simd_support &= JSIMD_ARM_NEON;
++ env = getenv("JSIMD_FORCENONE");
++ if ((env != NULL) && (strcmp(env, "1") == 0))
++ simd_support = 0;
++}
++
++GLOBAL(int)
++jsimd_can_rgb_ycc (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_rgb_gray (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_ycc_rgb (void)
++{
++ init_simd();
++
++ /* The code is optimised for these values only */
++ if (BITS_IN_JSAMPLE != 8)
++ return 0;
++ if (sizeof(JDIMENSION) != 4)
++ return 0;
++ if ((RGB_PIXELSIZE != 3) && (RGB_PIXELSIZE != 4))
++ return 0;
++
++ if (simd_support & JSIMD_ARM_NEON)
++ return 1;
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_ycc_rgb565 (void)
++{
++ init_simd();
++
++ /* The code is optimised for these values only */
++ if (BITS_IN_JSAMPLE != 8)
++ return 0;
++ if (sizeof(JDIMENSION) != 4)
++ return 0;
++
++ if (simd_support & JSIMD_ARM_NEON)
++ return 1;
++
++ return 0;
++}
++
++GLOBAL(void)
++jsimd_rgb_ycc_convert (j_compress_ptr cinfo,
++ JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
++ JDIMENSION output_row, int num_rows)
++{
++}
++
++GLOBAL(void)
++jsimd_rgb_gray_convert (j_compress_ptr cinfo,
++ JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
++ JDIMENSION output_row, int num_rows)
++{
++}
++
++GLOBAL(void)
++jsimd_ycc_rgb_convert (j_decompress_ptr cinfo,
++ JSAMPIMAGE input_buf, JDIMENSION input_row,
++ JSAMPARRAY output_buf, int num_rows)
++{
++ void (*neonfct)(JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY, int);
++
++ switch(cinfo->out_color_space) {
++ case JCS_EXT_RGB:
++ neonfct=jsimd_ycc_extrgb_convert_neon;
++ break;
++ case JCS_EXT_RGBX:
++ case JCS_EXT_RGBA:
++ neonfct=jsimd_ycc_extrgbx_convert_neon;
++ break;
++ case JCS_EXT_BGR:
++ neonfct=jsimd_ycc_extbgr_convert_neon;
++ break;
++ case JCS_EXT_BGRX:
++ case JCS_EXT_BGRA:
++ neonfct=jsimd_ycc_extbgrx_convert_neon;
++ break;
++ case JCS_EXT_XBGR:
++ case JCS_EXT_ABGR:
++ neonfct=jsimd_ycc_extxbgr_convert_neon;
++ break;
++ case JCS_EXT_XRGB:
++ case JCS_EXT_ARGB:
++ neonfct=jsimd_ycc_extxrgb_convert_neon;
++ break;
++ default:
++ neonfct=jsimd_ycc_extrgb_convert_neon;
++ break;
++ }
++
++ if (simd_support & JSIMD_ARM_NEON)
++ neonfct(cinfo->output_width, input_buf, input_row, output_buf, num_rows);
++}
++
++GLOBAL(void)
++jsimd_ycc_rgb565_convert (j_decompress_ptr cinfo,
++ JSAMPIMAGE input_buf, JDIMENSION input_row,
++ JSAMPARRAY output_buf, int num_rows)
++{
++ if (simd_support & JSIMD_ARM_NEON)
++ jsimd_ycc_rgb565_convert_neon(cinfo->output_width, input_buf, input_row,
++ output_buf, num_rows);
++}
++
++GLOBAL(int)
++jsimd_can_h2v2_downsample (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_h2v1_downsample (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(void)
++jsimd_h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
++ JSAMPARRAY input_data, JSAMPARRAY output_data)
++{
++}
++
++GLOBAL(void)
++jsimd_h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
++ JSAMPARRAY input_data, JSAMPARRAY output_data)
++{
++}
++
++GLOBAL(int)
++jsimd_can_h2v2_upsample (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_h2v1_upsample (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(void)
++jsimd_h2v2_upsample (j_decompress_ptr cinfo,
++ jpeg_component_info * compptr,
++ JSAMPARRAY input_data,
++ JSAMPARRAY * output_data_ptr)
++{
++}
++
++GLOBAL(void)
++jsimd_h2v1_upsample (j_decompress_ptr cinfo,
++ jpeg_component_info * compptr,
++ JSAMPARRAY input_data,
++ JSAMPARRAY * output_data_ptr)
++{
++}
++
++GLOBAL(int)
++jsimd_can_h2v2_fancy_upsample (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_h2v1_fancy_upsample (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(void)
++jsimd_h2v2_fancy_upsample (j_decompress_ptr cinfo,
++ jpeg_component_info * compptr,
++ JSAMPARRAY input_data,
++ JSAMPARRAY * output_data_ptr)
++{
++}
++
++GLOBAL(void)
++jsimd_h2v1_fancy_upsample (j_decompress_ptr cinfo,
++ jpeg_component_info * compptr,
++ JSAMPARRAY input_data,
++ JSAMPARRAY * output_data_ptr)
++{
++}
++
++GLOBAL(int)
++jsimd_can_h2v2_merged_upsample (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_h2v1_merged_upsample (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(void)
++jsimd_h2v2_merged_upsample (j_decompress_ptr cinfo,
++ JSAMPIMAGE input_buf,
++ JDIMENSION in_row_group_ctr,
++ JSAMPARRAY output_buf)
++{
++}
++
++GLOBAL(void)
++jsimd_h2v1_merged_upsample (j_decompress_ptr cinfo,
++ JSAMPIMAGE input_buf,
++ JDIMENSION in_row_group_ctr,
++ JSAMPARRAY output_buf)
++{
++}
++
++GLOBAL(int)
++jsimd_can_convsamp (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_convsamp_float (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(void)
++jsimd_convsamp (JSAMPARRAY sample_data, JDIMENSION start_col,
++ DCTELEM * workspace)
++{
++}
++
++GLOBAL(void)
++jsimd_convsamp_float (JSAMPARRAY sample_data, JDIMENSION start_col,
++ FAST_FLOAT * workspace)
++{
++}
++
++GLOBAL(int)
++jsimd_can_fdct_islow (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_fdct_ifast (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_fdct_float (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(void)
++jsimd_fdct_islow (DCTELEM * data)
++{
++}
++
++GLOBAL(void)
++jsimd_fdct_ifast (DCTELEM * data)
++{
++}
++
++GLOBAL(void)
++jsimd_fdct_float (FAST_FLOAT * data)
++{
++}
++
++GLOBAL(int)
++jsimd_can_quantize (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_quantize_float (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(void)
++jsimd_quantize (JCOEFPTR coef_block, DCTELEM * divisors,
++ DCTELEM * workspace)
++{
++}
++
++GLOBAL(void)
++jsimd_quantize_float (JCOEFPTR coef_block, FAST_FLOAT * divisors,
++ FAST_FLOAT * workspace)
++{
++}
++
++GLOBAL(int)
++jsimd_can_idct_2x2 (void)
++{
++ init_simd();
++
++ /* The code is optimised for these values only */
++ if (DCTSIZE != 8)
++ return 0;
++ if (sizeof(JCOEF) != 2)
++ return 0;
++ if (BITS_IN_JSAMPLE != 8)
++ return 0;
++ if (sizeof(JDIMENSION) != 4)
++ return 0;
++ if (sizeof(ISLOW_MULT_TYPE) != 2)
++ return 0;
++
++ if (simd_support & JSIMD_ARM_NEON)
++ return 1;
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_idct_4x4 (void)
++{
++ init_simd();
++
++ /* The code is optimised for these values only */
++ if (DCTSIZE != 8)
++ return 0;
++ if (sizeof(JCOEF) != 2)
++ return 0;
++ if (BITS_IN_JSAMPLE != 8)
++ return 0;
++ if (sizeof(JDIMENSION) != 4)
++ return 0;
++ if (sizeof(ISLOW_MULT_TYPE) != 2)
++ return 0;
++
++ if (simd_support & JSIMD_ARM_NEON)
++ return 1;
++
++ return 0;
++}
++
++GLOBAL(void)
++jsimd_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr,
++ JCOEFPTR coef_block, JSAMPARRAY output_buf,
++ JDIMENSION output_col)
++{
++ if (simd_support & JSIMD_ARM_NEON)
++ jsimd_idct_2x2_neon(compptr->dct_table, coef_block, output_buf,
++ output_col);
++}
++
++GLOBAL(void)
++jsimd_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr,
++ JCOEFPTR coef_block, JSAMPARRAY output_buf,
++ JDIMENSION output_col)
++{
++ if (simd_support & JSIMD_ARM_NEON)
++ jsimd_idct_4x4_neon(compptr->dct_table, coef_block, output_buf,
++ output_col);
++}
++
++GLOBAL(int)
++jsimd_can_idct_islow (void)
++{
++ init_simd();
++
++ /* The code is optimised for these values only */
++ if (DCTSIZE != 8)
++ return 0;
++ if (sizeof(JCOEF) != 2)
++ return 0;
++ if (BITS_IN_JSAMPLE != 8)
++ return 0;
++ if (sizeof(JDIMENSION) != 4)
++ return 0;
++ if (sizeof(ISLOW_MULT_TYPE) != 2)
++ return 0;
++
++ if (simd_support & JSIMD_ARM_NEON)
++ return 1;
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_idct_ifast (void)
++{
++ init_simd();
++
++ /* The code is optimised for these values only */
++ if (DCTSIZE != 8)
++ return 0;
++ if (sizeof(JCOEF) != 2)
++ return 0;
++ if (BITS_IN_JSAMPLE != 8)
++ return 0;
++ if (sizeof(JDIMENSION) != 4)
++ return 0;
++ if (sizeof(IFAST_MULT_TYPE) != 2)
++ return 0;
++ if (IFAST_SCALE_BITS != 2)
++ return 0;
++
++ if (simd_support & JSIMD_ARM_NEON)
++ return 1;
++
++ return 0;
++}
++
++GLOBAL(int)
++jsimd_can_idct_float (void)
++{
++ init_simd();
++
++ return 0;
++}
++
++GLOBAL(void)
++jsimd_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr,
++ JCOEFPTR coef_block, JSAMPARRAY output_buf,
++ JDIMENSION output_col)
++{
++ if (simd_support & JSIMD_ARM_NEON)
++ jsimd_idct_islow_neon(compptr->dct_table, coef_block, output_buf,
++ output_col);
++}
++
++GLOBAL(void)
++jsimd_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr,
++ JCOEFPTR coef_block, JSAMPARRAY output_buf,
++ JDIMENSION output_col)
++{
++ if (simd_support & JSIMD_ARM_NEON)
++ jsimd_idct_ifast_neon(compptr->dct_table, coef_block, output_buf,
++ output_col);
++}
++
++GLOBAL(void)
++jsimd_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr,
++ JCOEFPTR coef_block, JSAMPARRAY output_buf,
++ JDIMENSION output_col)
++{
++}
+Index: simd/jsimd_arm64_neon.S
+new file mode 100644
+===================================================================
+--- /dev/null
++++ simd/jsimd_arm64_neon.S
+@@ -0,0 +1,1861 @@
++/*
++ * ARMv8 NEON optimizations for libjpeg-turbo
++ *
++ * Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies).
++ * All rights reserved.
++ * Author: Siarhei Siamashka <siarhei.siamashka@nokia.com>
++ * Copyright (C) 2013-2014, Linaro Limited
++ * Author: Ragesh Radhakrishnan <ragesh.r@linaro.org>
++ *
++ * This software is provided 'as-is', without any express or implied
++ * warranty. In no event will the authors be held liable for any damages
++ * arising from the use of this software.
++ *
++ * Permission is granted to anyone to use this software for any purpose,
++ * including commercial applications, and to alter it and redistribute it
++ * freely, subject to the following restrictions:
++ *
++ * 1. The origin of this software must not be misrepresented; you must not
++ * claim that you wrote the original software. If you use this software
++ * in a product, an acknowledgment in the product documentation would be
++ * appreciated but is not required.
++ * 2. Altered source versions must be plainly marked as such, and must not be
++ * misrepresented as being the original software.
++ * 3. This notice may not be removed or altered from any source distribution.
++ */
++
++#if defined(__linux__) && defined(__ELF__)
++.section .note.GNU-stack,"",%progbits /* mark stack as non-executable */
++#endif
++
++.text
++.arch armv8-a+fp+simd
++
++
++#define RESPECT_STRICT_ALIGNMENT 1
++
++
++/*****************************************************************************/
++
++/* Supplementary macro for setting function attributes */
++.macro asm_function fname
++#ifdef __APPLE__
++ .globl _\fname
++_\fname:
++#else
++ .global \fname
++#ifdef __ELF__
++ .hidden \fname
++ .type \fname, %function
++#endif
++\fname:
++#endif
++.endm
++
++/* Transpose elements of single 128 bit registers */
++.macro transpose_single x0,x1,xi,xilen,literal
++ ins \xi\xilen[0], \x0\xilen[0]
++ ins \x1\xilen[0], \x0\xilen[1]
++ trn1 \x0\literal, \x0\literal, \x1\literal
++ trn2 \x1\literal, \xi\literal, \x1\literal
++.endm
++
++/* Transpose elements of 2 differnet registers */
++.macro transpose x0,x1,xi,xilen,literal
++ mov \xi\xilen, \x0\xilen
++ trn1 \x0\literal, \x0\literal, \x1\literal
++ trn2 \x1\literal, \xi\literal, \x1\literal
++.endm
++
++/* Transpose a block of 4x4 coefficients in four 64-bit registers */
++.macro transpose_4x4_32 x0,x0len x1,x1len x2,x2len x3,x3len,xi,xilen
++ mov \xi\xilen, \x0\xilen
++ trn1 \x0\x0len, \x0\x0len, \x2\x2len
++ trn2 \x2\x2len, \xi\x0len, \x2\x2len
++ mov \xi\xilen, \x1\xilen
++ trn1 \x1\x1len, \x1\x1len, \x3\x3len
++ trn2 \x3\x3len, \xi\x1len, \x3\x3len
++.endm
++
++.macro transpose_4x4_16 x0,x0len x1,x1len, x2,x2len, x3,x3len,xi,xilen
++ mov \xi\xilen, \x0\xilen
++ trn1 \x0\x0len, \x0\x0len, \x1\x1len
++ trn2 \x1\x2len, \xi\x0len, \x1\x2len
++ mov \xi\xilen, \x2\xilen
++ trn1 \x2\x2len, \x2\x2len, \x3\x3len
++ trn2 \x3\x2len, \xi\x1len, \x3\x3len
++.endm
++
++.macro transpose_4x4 x0, x1, x2, x3,x5
++ transpose_4x4_16 \x0,.4h, \x1,.4h, \x2,.4h,\x3,.4h,\x5,.16b
++ transpose_4x4_32 \x0,.2s, \x1,.2s, \x2,.2s,\x3,.2s,\x5,.16b
++.endm
++
++
++#define CENTERJSAMPLE 128
++
++/*****************************************************************************/
++
++/*
++ * Perform dequantization and inverse DCT on one block of coefficients.
++ *
++ * GLOBAL(void)
++ * jsimd_idct_islow_neon (void * dct_table, JCOEFPTR coef_block,
++ * JSAMPARRAY output_buf, JDIMENSION output_col)
++ */
++
++#define FIX_0_298631336 (2446)
++#define FIX_0_390180644 (3196)
++#define FIX_0_541196100 (4433)
++#define FIX_0_765366865 (6270)
++#define FIX_0_899976223 (7373)
++#define FIX_1_175875602 (9633)
++#define FIX_1_501321110 (12299)
++#define FIX_1_847759065 (15137)
++#define FIX_1_961570560 (16069)
++#define FIX_2_053119869 (16819)
++#define FIX_2_562915447 (20995)
++#define FIX_3_072711026 (25172)
++
++#define FIX_1_175875602_MINUS_1_961570560 (FIX_1_175875602 - FIX_1_961570560)
++#define FIX_1_175875602_MINUS_0_390180644 (FIX_1_175875602 - FIX_0_390180644)
++#define FIX_0_541196100_MINUS_1_847759065 (FIX_0_541196100 - FIX_1_847759065)
++#define FIX_3_072711026_MINUS_2_562915447 (FIX_3_072711026 - FIX_2_562915447)
++#define FIX_0_298631336_MINUS_0_899976223 (FIX_0_298631336 - FIX_0_899976223)
++#define FIX_1_501321110_MINUS_0_899976223 (FIX_1_501321110 - FIX_0_899976223)
++#define FIX_2_053119869_MINUS_2_562915447 (FIX_2_053119869 - FIX_2_562915447)
++#define FIX_0_541196100_PLUS_0_765366865 (FIX_0_541196100 + FIX_0_765366865)
++
++/*
++ * Reference SIMD-friendly 1-D ISLOW iDCT C implementation.
++ * Uses some ideas from the comments in 'simd/jiss2int-64.asm'
++ */
++#define REF_1D_IDCT(xrow0, xrow1, xrow2, xrow3, xrow4, xrow5, xrow6, xrow7) \
++{ \
++ DCTELEM row0, row1, row2, row3, row4, row5, row6, row7; \
++ INT32 q1, q2, q3, q4, q5, q6, q7; \
++ INT32 tmp11_plus_tmp2, tmp11_minus_tmp2; \
++ \
++ /* 1-D iDCT input data */ \
++ row0 = xrow0; \
++ row1 = xrow1; \
++ row2 = xrow2; \
++ row3 = xrow3; \
++ row4 = xrow4; \
++ row5 = xrow5; \
++ row6 = xrow6; \
++ row7 = xrow7; \
++ \
++ q5 = row7 + row3; \
++ q4 = row5 + row1; \
++ q6 = MULTIPLY(q5, FIX_1_175875602_MINUS_1_961570560) + \
++ MULTIPLY(q4, FIX_1_175875602); \
++ q7 = MULTIPLY(q5, FIX_1_175875602) + \
++ MULTIPLY(q4, FIX_1_175875602_MINUS_0_390180644); \
++ q2 = MULTIPLY(row2, FIX_0_541196100) + \
++ MULTIPLY(row6, FIX_0_541196100_MINUS_1_847759065); \
++ q4 = q6; \
++ q3 = ((INT32) row0 - (INT32) row4) << 13; \
++ q6 += MULTIPLY(row5, -FIX_2_562915447) + \
++ MULTIPLY(row3, FIX_3_072711026_MINUS_2_562915447); \
++ /* now we can use q1 (reloadable constants have been used up) */ \
++ q1 = q3 + q2; \
++ q4 += MULTIPLY(row7, FIX_0_298631336_MINUS_0_899976223) + \
++ MULTIPLY(row1, -FIX_0_899976223); \
++ q5 = q7; \
++ q1 = q1 + q6; \
++ q7 += MULTIPLY(row7, -FIX_0_899976223) + \
++ MULTIPLY(row1, FIX_1_501321110_MINUS_0_899976223); \
++ \
++ /* (tmp11 + tmp2) has been calculated (out_row1 before descale) */ \
++ tmp11_plus_tmp2 = q1; \
++ row1 = 0; \
++ \
++ q1 = q1 - q6; \
++ q5 += MULTIPLY(row5, FIX_2_053119869_MINUS_2_562915447) + \
++ MULTIPLY(row3, -FIX_2_562915447); \
++ q1 = q1 - q6; \
++ q6 = MULTIPLY(row2, FIX_0_541196100_PLUS_0_765366865) + \
++ MULTIPLY(row6, FIX_0_541196100); \
++ q3 = q3 - q2; \
++ \
++ /* (tmp11 - tmp2) has been calculated (out_row6 before descale) */ \
++ tmp11_minus_tmp2 = q1; \
++ \
++ q1 = ((INT32) row0 + (INT32) row4) << 13; \
++ q2 = q1 + q6; \
++ q1 = q1 - q6; \
++ \
++ /* pick up the results */ \
++ tmp0 = q4; \
++ tmp1 = q5; \
++ tmp2 = (tmp11_plus_tmp2 - tmp11_minus_tmp2) / 2; \
++ tmp3 = q7; \
++ tmp10 = q2; \
++ tmp11 = (tmp11_plus_tmp2 + tmp11_minus_tmp2) / 2; \
++ tmp12 = q3; \
++ tmp13 = q1; \
++}
++
++#define XFIX_0_899976223 v0.4h[0]
++#define XFIX_0_541196100 v0.4h[1]
++#define XFIX_2_562915447 v0.4h[2]
++#define XFIX_0_298631336_MINUS_0_899976223 v0.4h[3]
++#define XFIX_1_501321110_MINUS_0_899976223 v1.4h[0]
++#define XFIX_2_053119869_MINUS_2_562915447 v1.4h[1]
++#define XFIX_0_541196100_PLUS_0_765366865 v1.4h[2]
++#define XFIX_1_175875602 v1.4h[3]
++#define XFIX_1_175875602_MINUS_0_390180644 v2.4h[0]
++#define XFIX_0_541196100_MINUS_1_847759065 v2.4h[1]
++#define XFIX_3_072711026_MINUS_2_562915447 v2.4h[2]
++#define XFIX_1_175875602_MINUS_1_961570560 v2.4h[3]
++
++.balign 16
++jsimd_idct_islow_neon_consts:
++ .short FIX_0_899976223 /* d0[0] */
++ .short FIX_0_541196100 /* d0[1] */
++ .short FIX_2_562915447 /* d0[2] */
++ .short FIX_0_298631336_MINUS_0_899976223 /* d0[3] */
++ .short FIX_1_501321110_MINUS_0_899976223 /* d1[0] */
++ .short FIX_2_053119869_MINUS_2_562915447 /* d1[1] */
++ .short FIX_0_541196100_PLUS_0_765366865 /* d1[2] */
++ .short FIX_1_175875602 /* d1[3] */
++ /* reloadable constants */
++ .short FIX_1_175875602_MINUS_0_390180644 /* d2[0] */
++ .short FIX_0_541196100_MINUS_1_847759065 /* d2[1] */
++ .short FIX_3_072711026_MINUS_2_562915447 /* d2[2] */
++ .short FIX_1_175875602_MINUS_1_961570560 /* d2[3] */
++
++asm_function jsimd_idct_islow_neon
++
++ DCT_TABLE .req x0
++ COEF_BLOCK .req x1
++ OUTPUT_BUF .req x2
++ OUTPUT_COL .req x3
++ TMP1 .req x0
++ TMP2 .req x1
++ TMP3 .req x2
++ TMP4 .req x15
++
++ ROW0L .req v16
++ ROW0R .req v17
++ ROW1L .req v18
++ ROW1R .req v19
++ ROW2L .req v20
++ ROW2R .req v21
++ ROW3L .req v22
++ ROW3R .req v23
++ ROW4L .req v24
++ ROW4R .req v25
++ ROW5L .req v26
++ ROW5R .req v27
++ ROW6L .req v28
++ ROW6R .req v29
++ ROW7L .req v30
++ ROW7R .req v31
++ /* Save all NEON registers and x15 (32 NEON registers * 8 bytes + 16) */
++ sub sp, sp, 272
++ str x15, [sp], 16
++ adr x15, jsimd_idct_islow_neon_consts
++ st1 {v0.8b - v3.8b}, [sp], 32
++ st1 {v4.8b - v7.8b}, [sp], 32
++ st1 {v8.8b - v11.8b}, [sp], 32
++ st1 {v12.8b - v15.8b}, [sp], 32
++ st1 {v16.8b - v19.8b}, [sp], 32
++ st1 {v20.8b - v23.8b}, [sp], 32
++ st1 {v24.8b - v27.8b}, [sp], 32
++ st1 {v28.8b - v31.8b}, [sp], 32
++ ld1 {v16.4h, v17.4h, v18.4h, v19.4h}, [COEF_BLOCK], 32
++ ld1 {v0.4h, v1.4h, v2.4h, v3.4h}, [DCT_TABLE], 32
++ ld1 {v20.4h, v21.4h, v22.4h, v23.4h}, [COEF_BLOCK], 32
++ mul v16.4h, v16.4h, v0.4h
++ mul v17.4h, v17.4h, v1.4h
++ ins v16.2d[1], v17.2d[0] /* 128 bit q8 */
++ ld1 {v4.4h, v5.4h, v6.4h, v7.4h}, [DCT_TABLE], 32
++ mul v18.4h, v18.4h, v2.4h
++ mul v19.4h, v19.4h, v3.4h
++ ins v18.2d[1], v19.2d[0] /* 128 bit q9 */
++ ld1 {v24.4h, v25.4h, v26.4h, v27.4h}, [COEF_BLOCK], 32
++ mul v20.4h, v20.4h, v4.4h
++ mul v21.4h, v21.4h, v5.4h
++ ins v20.2d[1], v21.2d[0] /* 128 bit q10 */
++ ld1 {v0.4h, v1.4h, v2.4h, v3.4h}, [DCT_TABLE], 32
++ mul v22.4h, v22.4h, v6.4h
++ mul v23.4h, v23.4h, v7.4h
++ ins v22.2d[1], v23.2d[0] /* 128 bit q11 */
++ ld1 {v28.4h, v29.4h, v30.4h, v31.4h}, [COEF_BLOCK]
++ mul v24.4h, v24.4h, v0.4h
++ mul v25.4h, v25.4h, v1.4h
++ ins v24.2d[1], v25.2d[0] /* 128 bit q12 */
++ ld1 {v4.4h, v5.4h, v6.4h, v7.4h}, [DCT_TABLE], 32
++ mul v28.4h, v28.4h, v4.4h
++ mul v29.4h, v29.4h, v5.4h
++ ins v28.2d[1], v29.2d[0] /* 128 bit q14 */
++ mul v26.4h, v26.4h, v2.4h
++ mul v27.4h, v27.4h, v3.4h
++ ins v26.2d[1], v27.2d[0] /* 128 bit q13 */
++ ld1 {v0.4h, v1.4h, v2.4h, v3.4h}, [x15] /* load constants */
++ add x15, x15, #16
++ mul v30.4h, v30.4h, v6.4h
++ mul v31.4h, v31.4h, v7.4h
++ ins v30.2d[1], v31.2d[0] /* 128 bit q15 */
++ /* Go to the bottom of the stack */
++ sub sp, sp, 352
++ stp x4, x5, [sp], 16
++ st1 {v8.4h - v11.4h}, [sp], 32 /* save NEON registers */
++ st1 {v12.4h - v15.4h}, [sp], 32
++ /* 1-D IDCT, pass 1, left 4x8 half */
++ add v4.4h, ROW7L.4h, ROW3L.4h
++ add v5.4h, ROW5L.4h, ROW1L.4h
++ smull v12.4s, v4.4h, XFIX_1_175875602_MINUS_1_961570560
++ smlal v12.4s, v5.4h, XFIX_1_175875602
++ smull v14.4s, v4.4h, XFIX_1_175875602
++ /* Check for the zero coefficients in the right 4x8 half */
++ smlal v14.4s, v5.4h, XFIX_1_175875602_MINUS_0_390180644
++ ssubl v6.4s, ROW0L.4h, ROW4L.4h
++ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 1 * 8))]
++ smull v4.4s, ROW2L.4h, XFIX_0_541196100
++ smlal v4.4s, ROW6L.4h, XFIX_0_541196100_MINUS_1_847759065
++ orr x0, x4, x5
++ mov v8.16b, v12.16b
++ smlsl v12.4s, ROW5L.4h, XFIX_2_562915447
++ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 2 * 8))]
++ smlal v12.4s, ROW3L.4h, XFIX_3_072711026_MINUS_2_562915447
++ shl v6.4s, v6.4s, #13
++ orr x0, x0, x4
++ smlsl v8.4s, ROW1L.4h, XFIX_0_899976223
++ orr x0, x0 , x5
++ add v2.4s, v6.4s, v4.4s
++ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 3 * 8))]
++ mov v10.16b, v14.16b
++ add v2.4s, v2.4s, v12.4s
++ orr x0, x0, x4
++ smlsl v14.4s, ROW7L.4h, XFIX_0_899976223
++ orr x0, x0, x5
++ smlal v14.4s, ROW1L.4h, XFIX_1_501321110_MINUS_0_899976223
++ rshrn ROW1L.4h, v2.4s, #11
++ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 4 * 8))]
++ sub v2.4s, v2.4s, v12.4s
++ smlal v10.4s, ROW5L.4h, XFIX_2_053119869_MINUS_2_562915447
++ orr x0, x0, x4
++ smlsl v10.4s, ROW3L.4h, XFIX_2_562915447
++ orr x0, x0, x5
++ sub v2.4s, v2.4s, v12.4s
++ smull v12.4s, ROW2L.4h, XFIX_0_541196100_PLUS_0_765366865
++ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 5 * 8))]
++ smlal v12.4s, ROW6L.4h, XFIX_0_541196100
++ sub v6.4s, v6.4s, v4.4s
++ orr x0, x0, x4
++ rshrn ROW6L.4h, v2.4s, #11
++ orr x0, x0, x5
++ add v2.4s, v6.4s, v10.4s
++ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 6 * 8))]
++ sub v6.4s, v6.4s, v10.4s
++ saddl v10.4s, ROW0L.4h, ROW4L.4h
++ orr x0, x0, x4
++ rshrn ROW2L.4h, v2.4s, #11
++ orr x0, x0, x5
++ rshrn ROW5L.4h, v6.4s, #11
++ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 7 * 8))]
++ shl v10.4s, v10.4s, #13
++ smlal v8.4s, ROW7L.4h, XFIX_0_298631336_MINUS_0_899976223
++ orr x0, x0, x4
++ add v4.4s, v10.4s, v12.4s
++ orr x0, x0, x5
++ cmp x0, #0 /* orrs instruction removed */
++ sub v2.4s, v10.4s, v12.4s
++ add v12.4s, v4.4s, v14.4s
++ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 0 * 8))]
++ sub v4.4s, v4.4s, v14.4s
++ add v10.4s, v2.4s, v8.4s
++ orr x0, x4, x5
++ sub v6.4s, v2.4s, v8.4s
++ /* pop {x4, x5} */
++ sub sp, sp, 80
++ ldp x4, x5, [sp], 16
++ rshrn ROW7L.4h, v4.4s, #11
++ rshrn ROW3L.4h, v10.4s, #11
++ rshrn ROW0L.4h, v12.4s, #11
++ rshrn ROW4L.4h, v6.4s, #11
++
++ beq 3f /* Go to do some special handling for the sparse right 4x8 half */
++
++ /* 1-D IDCT, pass 1, right 4x8 half */
++ ld1 {v2.4h}, [x15] /* reload constants */
++ add v10.4h, ROW7R.4h, ROW3R.4h
++ add v8.4h, ROW5R.4h, ROW1R.4h
++ /* Transpose ROW6L <-> ROW7L (v3 available free register) */
++ transpose ROW6L, ROW7L, v3, .16b, .4h
++ smull v12.4s, v10.4h, XFIX_1_175875602_MINUS_1_961570560
++ smlal v12.4s, v8.4h, XFIX_1_175875602
++ /* Transpose ROW2L <-> ROW3L (v3 available free register) */
++ transpose ROW2L, ROW3L, v3, .16b, .4h
++ smull v14.4s, v10.4h, XFIX_1_175875602
++ smlal v14.4s, v8.4h, XFIX_1_175875602_MINUS_0_390180644
++ /* Transpose ROW0L <-> ROW1L (v3 available free register) */
++ transpose ROW0L, ROW1L, v3, .16b, .4h
++ ssubl v6.4s, ROW0R.4h, ROW4R.4h
++ smull v4.4s, ROW2R.4h, XFIX_0_541196100
++ smlal v4.4s, ROW6R.4h, XFIX_0_541196100_MINUS_1_847759065
++ /* Transpose ROW4L <-> ROW5L (v3 available free register) */
++ transpose ROW4L, ROW5L, v3, .16b, .4h
++ mov v8.16b, v12.16b
++ smlsl v12.4s, ROW5R.4h, XFIX_2_562915447
++ smlal v12.4s, ROW3R.4h, XFIX_3_072711026_MINUS_2_562915447
++ /* Transpose ROW1L <-> ROW3L (v3 available free register) */
++ transpose ROW1L, ROW3L, v3, .16b, .2s
++ shl v6.4s, v6.4s, #13
++ smlsl v8.4s, ROW1R.4h, XFIX_0_899976223
++ /* Transpose ROW4L <-> ROW6L (v3 available free register) */
++ transpose ROW4L, ROW6L, v3, .16b, .2s
++ add v2.4s, v6.4s, v4.4s
++ mov v10.16b, v14.16b
++ add v2.4s, v2.4s, v12.4s
++ /* Transpose ROW0L <-> ROW2L (v3 available free register) */
++ transpose ROW0L, ROW2L, v3, .16b, .2s
++ smlsl v14.4s, ROW7R.4h, XFIX_0_899976223
++ smlal v14.4s, ROW1R.4h, XFIX_1_501321110_MINUS_0_899976223
++ rshrn ROW1R.4h, v2.4s, #11
++ /* Transpose ROW5L <-> ROW7L (v3 available free register) */
++ transpose ROW5L, ROW7L, v3, .16b, .2s
++ sub v2.4s, v2.4s, v12.4s
++ smlal v10.4s, ROW5R.4h, XFIX_2_053119869_MINUS_2_562915447
++ smlsl v10.4s, ROW3R.4h, XFIX_2_562915447
++ sub v2.4s, v2.4s, v12.4s
++ smull v12.4s, ROW2R.4h, XFIX_0_541196100_PLUS_0_765366865
++ smlal v12.4s, ROW6R.4h, XFIX_0_541196100
++ sub v6.4s, v6.4s, v4.4s
++ rshrn ROW6R.4h, v2.4s, #11
++ add v2.4s, v6.4s, v10.4s
++ sub v6.4s, v6.4s, v10.4s
++ saddl v10.4s, ROW0R.4h, ROW4R.4h
++ rshrn ROW2R.4h, v2.4s, #11
++ rshrn ROW5R.4h, v6.4s, #11
++ shl v10.4s, v10.4s, #13
++ smlal v8.4s, ROW7R.4h, XFIX_0_298631336_MINUS_0_899976223
++ add v4.4s, v10.4s, v12.4s
++ sub v2.4s, v10.4s, v12.4s
++ add v12.4s, v4.4s, v14.4s
++ sub v4.4s, v4.4s, v14.4s
++ add v10.4s, v2.4s, v8.4s
++ sub v6.4s, v2.4s, v8.4s
++ rshrn ROW7R.4h, v4.4s, #11
++ rshrn ROW3R.4h, v10.4s, #11
++ rshrn ROW0R.4h, v12.4s, #11
++ rshrn ROW4R.4h, v6.4s, #11
++ /* Transpose right 4x8 half */
++ transpose ROW6R, ROW7R, v3, .16b, .4h
++ transpose ROW2R, ROW3R, v3, .16b, .4h
++ transpose ROW0R, ROW1R, v3, .16b, .4h
++ transpose ROW4R, ROW5R, v3, .16b, .4h
++ transpose ROW1R, ROW3R, v3, .16b, .2s
++ transpose ROW4R, ROW6R, v3, .16b, .2s
++ transpose ROW0R, ROW2R, v3, .16b, .2s
++ transpose ROW5R, ROW7R, v3, .16b, .2s
++
++1: /* 1-D IDCT, pass 2 (normal variant), left 4x8 half */
++ ld1 {v2.4h}, [x15] /* reload constants */
++ smull v12.4S, ROW1R.4h, XFIX_1_175875602 /* ROW5L.4h <-> ROW1R.4h */
++ smlal v12.4s, ROW1L.4h, XFIX_1_175875602
++ smlal v12.4s, ROW3R.4h, XFIX_1_175875602_MINUS_1_961570560 /* ROW7L.4h <-> ROW3R.4h */
++ smlal v12.4s, ROW3L.4h, XFIX_1_175875602_MINUS_1_961570560
++ smull v14.4s, ROW3R.4h, XFIX_1_175875602 /* ROW7L.4h <-> ROW3R.4h */
++ smlal v14.4s, ROW3L.4h, XFIX_1_175875602
++ smlal v14.4s, ROW1R.4h, XFIX_1_175875602_MINUS_0_390180644 /* ROW5L.4h <-> ROW1R.4h */
++ smlal v14.4s, ROW1L.4h, XFIX_1_175875602_MINUS_0_390180644
++ ssubl v6.4s, ROW0L.4h, ROW0R.4h /* ROW4L.4h <-> ROW0R.4h */
++ smull v4.4s, ROW2L.4h, XFIX_0_541196100
++ smlal v4.4s, ROW2R.4h, XFIX_0_541196100_MINUS_1_847759065 /* ROW6L.4h <-> ROW2R.4h */
++ mov v8.16b, v12.16b
++ smlsl v12.4s, ROW1R.4h, XFIX_2_562915447 /* ROW5L.4h <-> ROW1R.4h */
++ smlal v12.4s, ROW3L.4h, XFIX_3_072711026_MINUS_2_562915447
++ shl v6.4s, v6.4s, #13
++ smlsl v8.4s, ROW1L.4h, XFIX_0_899976223
++ add v2.4s, v6.4s, v4.4s
++ mov v10.16b, v14.16b
++ add v2.4s, v2.4s, v12.4s
++ smlsl v14.4s, ROW3R.4h, XFIX_0_899976223 /* ROW7L.4h <-> ROW3R.4h */
++ smlal v14.4s, ROW1L.4h, XFIX_1_501321110_MINUS_0_899976223
++ shrn ROW1L.4h, v2.4s, #16
++ sub v2.4s, v2.4s, v12.4s
++ smlal v10.4s, ROW1R.4h, XFIX_2_053119869_MINUS_2_562915447 /* ROW5L.4h <-> ROW1R.4h */
++ smlsl v10.4s, ROW3L.4h, XFIX_2_562915447
++ sub v2.4s, v2.4s, v12.4s
++ smull v12.4s, ROW2L.4h, XFIX_0_541196100_PLUS_0_765366865
++ smlal v12.4s, ROW2R.4h, XFIX_0_541196100 /* ROW6L.4h <-> ROW2R.4h */
++ sub v6.4s, v6.4s, v4.4s
++ shrn ROW2R.4h, v2.4s, #16 /* ROW6L.4h <-> ROW2R.4h */
++ add v2.4s, v6.4s, v10.4s
++ sub v6.4s, v6.4s, v10.4s
++ saddl v10.4s, ROW0L.4h, ROW0R.4h /* ROW4L.4h <-> ROW0R.4h */
++ shrn ROW2L.4h, v2.4s, #16
++ shrn ROW1R.4h, v6.4s, #16 /* ROW5L.4h <-> ROW1R.4h */
++ shl v10.4s, v10.4s, #13
++ smlal v8.4s, ROW3R.4h, XFIX_0_298631336_MINUS_0_899976223 /* ROW7L.4h <-> ROW3R.4h */
++ add v4.4s, v10.4s, v12.4s
++ sub v2.4s, v10.4s, v12.4s
++ add v12.4s, v4.4s, v14.4s
++ sub v4.4s, v4.4s, v14.4s
++ add v10.4s, v2.4s, v8.4s
++ sub v6.4s, v2.4s, v8.4s
++ shrn ROW3R.4h, v4.4s, #16 /* ROW7L.4h <-> ROW3R.4h */
++ shrn ROW3L.4h, v10.4s, #16
++ shrn ROW0L.4h, v12.4s, #16
++ shrn ROW0R.4h, v6.4s, #16 /* ROW4L.4h <-> ROW0R.4h */
++ /* 1-D IDCT, pass 2, right 4x8 half */
++ ld1 {v2.4h}, [x15] /* reload constants */
++ smull v12.4s, ROW5R.4h, XFIX_1_175875602
++ smlal v12.4s, ROW5L.4h, XFIX_1_175875602 /* ROW5L.4h <-> ROW1R.4h */
++ smlal v12.4s, ROW7R.4h, XFIX_1_175875602_MINUS_1_961570560
++ smlal v12.4s, ROW7L.4h, XFIX_1_175875602_MINUS_1_961570560 /* ROW7L.4h <-> ROW3R.4h */
++ smull v14.4s, ROW7R.4h, XFIX_1_175875602
++ smlal v14.4s, ROW7L.4h, XFIX_1_175875602 /* ROW7L.4h <-> ROW3R.4h */
++ smlal v14.4s, ROW5R.4h, XFIX_1_175875602_MINUS_0_390180644
++ smlal v14.4s, ROW5L.4h, XFIX_1_175875602_MINUS_0_390180644 /* ROW5L.4h <-> ROW1R.4h */
++ ssubl v6.4s, ROW4L.4h, ROW4R.4h /* ROW4L.4h <-> ROW0R.4h */
++ smull v4.4s, ROW6L.4h, XFIX_0_541196100 /* ROW6L.4h <-> ROW2R.4h */
++ smlal v4.4s, ROW6R.4h, XFIX_0_541196100_MINUS_1_847759065
++ mov v8.16b, v12.16b
++ smlsl v12.4s, ROW5R.4h, XFIX_2_562915447
++ smlal v12.4s, ROW7L.4h, XFIX_3_072711026_MINUS_2_562915447 /* ROW7L.4h <-> ROW3R.4h */
++ shl v6.4s, v6.4s, #13
++ smlsl v8.4s, ROW5L.4h, XFIX_0_899976223 /* ROW5L.4h <-> ROW1R.4h */
++ add v2.4s, v6.4s, v4.4s
++ mov v10.16b, v14.16b
++ add v2.4s, v2.4s, v12.4s
++ smlsl v14.4s, ROW7R.4h, XFIX_0_899976223
++ smlal v14.4s, ROW5L.4h, XFIX_1_501321110_MINUS_0_899976223 /* ROW5L.4h <-> ROW1R.4h */
++ shrn ROW5L.4h, v2.4s, #16 /* ROW5L.4h <-> ROW1R.4h */
++ sub v2.4s, v2.4s, v12.4s
++ smlal v10.4s, ROW5R.4h, XFIX_2_053119869_MINUS_2_562915447
++ smlsl v10.4s, ROW7L.4h, XFIX_2_562915447 /* ROW7L.4h <-> ROW3R.4h */
++ sub v2.4s, v2.4s, v12.4s
++ smull v12.4s, ROW6L.4h, XFIX_0_541196100_PLUS_0_765366865 /* ROW6L.4h <-> ROW2R.4h */
++ smlal v12.4s, ROW6R.4h, XFIX_0_541196100
++ sub v6.4s, v6.4s, v4.4s
++ shrn ROW6R.4h, v2.4s, #16
++ add v2.4s, v6.4s, v10.4s
++ sub v6.4s, v6.4s, v10.4s
++ saddl v10.4s, ROW4L.4h, ROW4R.4h /* ROW4L.4h <-> ROW0R.4h */
++ shrn ROW6L.4h, v2.4s, #16 /* ROW6L.4h <-> ROW2R.4h */
++ shrn ROW5R.4h, v6.4s, #16
++ shl v10.4s, v10.4s, #13
++ smlal v8.4s, ROW7R.4h, XFIX_0_298631336_MINUS_0_899976223
++ add v4.4s, v10.4s, v12.4s
++ sub v2.4s, v10.4s, v12.4s
++ add v12.4s, v4.4s, v14.4s
++ sub v4.4s, v4.4s, v14.4s
++ add v10.4s, v2.4s, v8.4s
++ sub v6.4s, v2.4s, v8.4s
++ shrn ROW7R.4h, v4.4s, #16
++ shrn ROW7L.4h, v10.4s, #16 /* ROW7L.4h <-> ROW3R.4h */
++ shrn ROW4L.4h, v12.4s, #16 /* ROW4L.4h <-> ROW0R.4h */
++ shrn ROW4R.4h, v6.4s, #16
++
++2: /* Descale to 8-bit and range limit */
++ ins v16.2d[1], v17.2d[0]
++ ins v18.2d[1], v19.2d[0]
++ ins v20.2d[1], v21.2d[0]
++ ins v22.2d[1], v23.2d[0]
++ sqrshrn v16.8b, v16.8h, #2
++ sqrshrn2 v16.16b, v18.8h, #2
++ sqrshrn v18.8b, v20.8h, #2
++ sqrshrn2 v18.16b, v22.8h, #2
++
++ /* vpop {v8.4h - d15.4h} */ /* restore NEON registers */
++ ld1 {v8.4h - v11.4h}, [sp], 32
++ ld1 {v12.4h - v15.4h}, [sp], 32
++ ins v24.2d[1], v25.2d[0]
++
++ sqrshrn v20.8b, v24.8h, #2
++ /* Transpose the final 8-bit samples and do signed->unsigned conversion */
++ /* trn1 v16.8h, v16.8h, v18.8h */
++ transpose v16, v18, v3, .16b, .8h
++ ins v26.2d[1], v27.2d[0]
++ ins v28.2d[1], v29.2d[0]
++ ins v30.2d[1], v31.2d[0]
++ sqrshrn2 v20.16b, v26.8h, #2
++ sqrshrn v22.8b, v28.8h, #2
++ movi v0.16b, #(CENTERJSAMPLE)
++ sqrshrn2 v22.16b, v30.8h, #2
++ transpose_single v16, v17, v3, .2d, .8b
++ transpose_single v18, v19, v3, .2d, .8b
++ add v16.8b, v16.8b, v0.8b
++ add v17.8b, v17.8b, v0.8b
++ add v18.8b, v18.8b, v0.8b
++ add v19.8b, v19.8b, v0.8b
++ transpose v20, v22, v3, .16b, .8h
++ /* Store results to the output buffer */
++ ldp TMP1, TMP2, [OUTPUT_BUF], 16
++ add TMP1, TMP1, OUTPUT_COL
++ add TMP2, TMP2, OUTPUT_COL
++ st1 {v16.8b}, [TMP1]
++ transpose_single v20, v21, v3, .2d, .8b
++ st1 {v17.8b}, [TMP2]
++ ldp TMP1, TMP2, [OUTPUT_BUF], 16
++ add TMP1, TMP1, OUTPUT_COL
++ add TMP2, TMP2, OUTPUT_COL
++ st1 {v18.8b}, [TMP1]
++ add v20.8b, v20.8b, v0.8b
++ add v21.8b, v21.8b, v0.8b
++ st1 {v19.8b}, [TMP2]
++ ldp TMP1, TMP2, [OUTPUT_BUF], 16
++ ldp TMP3, TMP4, [OUTPUT_BUF]
++ add TMP1, TMP1, OUTPUT_COL
++ add TMP2, TMP2, OUTPUT_COL
++ add TMP3, TMP3, OUTPUT_COL
++ add TMP4, TMP4, OUTPUT_COL
++ transpose_single v22, v23, v3, .2d, .8b
++ st1 {v20.8b}, [TMP1]
++ add v22.8b, v22.8b, v0.8b
++ add v23.8b, v23.8b, v0.8b
++ st1 {v21.8b}, [TMP2]
++ st1 {v22.8b}, [TMP3]
++ st1 {v23.8b}, [TMP4]
++ ldr x15, [sp], 16
++ ld1 {v0.8b - v3.8b}, [sp], 32
++ ld1 {v4.8b - v7.8b}, [sp], 32
++ ld1 {v8.8b - v11.8b}, [sp], 32
++ ld1 {v12.8b - v15.8b}, [sp], 32
++ ld1 {v16.8b - v19.8b}, [sp], 32
++ ld1 {v20.8b - v23.8b}, [sp], 32
++ ld1 {v24.8b - v27.8b}, [sp], 32
++ ld1 {v28.8b - v31.8b}, [sp], 32
++ blr x30
++
++3: /* Left 4x8 half is done, right 4x8 half contains mostly zeros */
++
++ /* Transpose left 4x8 half */
++ transpose ROW6L, ROW7L, v3, .16b, .4h
++ transpose ROW2L, ROW3L, v3, .16b, .4h
++ transpose ROW0L, ROW1L, v3, .16b, .4h
++ transpose ROW4L, ROW5L, v3, .16b, .4h
++ shl ROW0R.4h, ROW0R.4h, #2 /* PASS1_BITS */
++ transpose ROW1L, ROW3L, v3, .16b, .2s
++ transpose ROW4L, ROW6L, v3, .16b, .2s
++ transpose ROW0L, ROW2L, v3, .16b, .2s
++ transpose ROW5L, ROW7L, v3, .16b, .2s
++ cmp x0, #0
++ beq 4f /* Right 4x8 half has all zeros, go to 'sparse' second pass */
++
++ /* Only row 0 is non-zero for the right 4x8 half */
++ dup ROW1R.4h, ROW0R.4h[1]
++ dup ROW2R.4h, ROW0R.4h[2]
++ dup ROW3R.4h, ROW0R.4h[3]
++ dup ROW4R.4h, ROW0R.4h[0]
++ dup ROW5R.4h, ROW0R.4h[1]
++ dup ROW6R.4h, ROW0R.4h[2]
++ dup ROW7R.4h, ROW0R.4h[3]
++ dup ROW0R.4h, ROW0R.4h[0]
++ b 1b /* Go to 'normal' second pass */
++
++4: /* 1-D IDCT, pass 2 (sparse variant with zero rows 4-7), left 4x8 half */
++ ld1 {v2.4h}, [x15] /* reload constants */
++ smull v12.4s, ROW1L.4h, XFIX_1_175875602
++ smlal v12.4s, ROW3L.4h, XFIX_1_175875602_MINUS_1_961570560
++ smull v14.4s, ROW3L.4h, XFIX_1_175875602
++ smlal v14.4s, ROW1L.4h, XFIX_1_175875602_MINUS_0_390180644
++ smull v4.4s, ROW2L.4h, XFIX_0_541196100
++ sshll v6.4s, ROW0L.4h, #13
++ mov v8.16b, v12.16b
++ smlal v12.4s, ROW3L.4h, XFIX_3_072711026_MINUS_2_562915447
++ smlsl v8.4s, ROW1L.4h, XFIX_0_899976223
++ add v2.4s, v6.4s, v4.4s
++ mov v10.16b, v14.16b
++ smlal v14.4s, ROW1L.4h, XFIX_1_501321110_MINUS_0_899976223
++ add v2.4s, v2.4s, v12.4s
++ add v12.4s, v12.4s, v12.4s
++ smlsl v10.4s, ROW3L.4h, XFIX_2_562915447
++ shrn ROW1L.4h, v2.4s, #16
++ sub v2.4s, v2.4s, v12.4s
++ smull v12.4s, ROW2L.4h, XFIX_0_541196100_PLUS_0_765366865
++ sub v6.4s, v6.4s, v4.4s
++ shrn ROW2R.4h, v2.4s, #16 /* ROW6L.4h <-> ROW2R.4h */
++ add v2.4s, v6.4s, v10.4s
++ sub v6.4s, v6.4s, v10.4s
++ sshll v10.4s, ROW0L.4h, #13
++ shrn ROW2L.4h, v2.4s, #16
++ shrn ROW1R.4h, v6.4s, #16 /* ROW5L.4h <-> ROW1R.4h */
++ add v4.4s, v10.4s, v12.4s
++ sub v2.4s, v10.4s, v12.4s
++ add v12.4s, v4.4s, v14.4s
++ sub v4.4s, v4.4s, v14.4s
++ add v10.4s, v2.4s, v8.4s
++ sub v6.4s, v2.4s, v8.4s
++ shrn ROW3R.4h, v4.4s, #16 /* ROW7L.4h <-> ROW3R.4h */
++ shrn ROW3L.4h, v10.4s, #16
++ shrn ROW0L.4h, v12.4s, #16
++ shrn ROW0R.4h, v6.4s, #16 /* ROW4L.4h <-> ROW0R.4h */
++ /* 1-D IDCT, pass 2 (sparse variant with zero rows 4-7), right 4x8 half */
++ ld1 {v2.4h}, [x15] /* reload constants */
++ smull v12.4s, ROW5L.4h, XFIX_1_175875602
++ smlal v12.4s, ROW7L.4h, XFIX_1_175875602_MINUS_1_961570560
++ smull v14.4s, ROW7L.4h, XFIX_1_175875602
++ smlal v14.4s, ROW5L.4h, XFIX_1_175875602_MINUS_0_390180644
++ smull v4.4s, ROW6L.4h, XFIX_0_541196100
++ sshll v6.4s, ROW4L.4h, #13
++ mov v8.16b, v12.16b
++ smlal v12.4s, ROW7L.4h, XFIX_3_072711026_MINUS_2_562915447
++ smlsl v8.4s, ROW5L.4h, XFIX_0_899976223
++ add v2.4s, v6.4s, v4.4s
++ mov v10.16b, v14.16b
++ smlal v14.4s, ROW5L.4h, XFIX_1_501321110_MINUS_0_899976223
++ add v2.4s, v2.4s, v12.4s
++ add v12.4s, v12.4s, v12.4s
++ smlsl v10.4s, ROW7L.4h, XFIX_2_562915447
++ shrn ROW5L.4h, v2.4s, #16 /* ROW5L.4h <-> ROW1R.4h */
++ sub v2.4s, v2.4s, v12.4s
++ smull v12.4s, ROW6L.4h, XFIX_0_541196100_PLUS_0_765366865
++ sub v6.4s, v6.4s, v4.4s
++ shrn ROW6R.4h, v2.4s, #16
++ add v2.4s, v6.4s, v10.4s
++ sub v6.4s, v6.4s, v10.4s
++ sshll v10.4s, ROW4L.4h, #13
++ shrn ROW6L.4h, v2.4s, #16 /* ROW6L.4h <-> ROW2R.4h */
++ shrn ROW5R.4h, v6.4s, #16
++ add v4.4s, v10.4s, v12.4s
++ sub v2.4s, v10.4s, v12.4s
++ add v12.4s, v4.4s, v14.4s
++ sub v4.4s, v4.4s, v14.4s
++ add v10.4s, v2.4s, v8.4s
++ sub v6.4s, v2.4s, v8.4s
++ shrn ROW7R.4h, v4.4s, #16
++ shrn ROW7L.4h, v10.4s, #16 /* ROW7L.4h <-> ROW3R.4h */
++ shrn ROW4L.4h, v12.4s, #16 /* ROW4L.4h <-> ROW0R.4h */
++ shrn ROW4R.4h, v6.4s, #16
++ b 2b /* Go to epilogue */
++
++ .unreq DCT_TABLE
++ .unreq COEF_BLOCK
++ .unreq OUTPUT_BUF
++ .unreq OUTPUT_COL
++ .unreq TMP1
++ .unreq TMP2
++ .unreq TMP3
++ .unreq TMP4
++
++ .unreq ROW0L
++ .unreq ROW0R
++ .unreq ROW1L
++ .unreq ROW1R
++ .unreq ROW2L
++ .unreq ROW2R
++ .unreq ROW3L
++ .unreq ROW3R
++ .unreq ROW4L
++ .unreq ROW4R
++ .unreq ROW5L
++ .unreq ROW5R
++ .unreq ROW6L
++ .unreq ROW6R
++ .unreq ROW7L
++ .unreq ROW7R
++
++
++/*****************************************************************************/
++
++/*
++ * jsimd_idct_ifast_neon
++ *
++ * This function contains a fast, not so accurate integer implementation of
++ * the inverse DCT (Discrete Cosine Transform). It uses the same calculations
++ * and produces exactly the same output as IJG's original 'jpeg_idct_ifast'
++ * function from jidctfst.c
++ *
++ * Normally 1-D AAN DCT needs 5 multiplications and 29 additions.
++ * But in ARM NEON case some extra additions are required because VQDMULH
++ * instruction can't handle the constants larger than 1. So the expressions
++ * like "x * 1.082392200" have to be converted to "x * 0.082392200 + x",
++ * which introduces an extra addition. Overall, there are 6 extra additions
++ * per 1-D IDCT pass, totalling to 5 VQDMULH and 35 VADD/VSUB instructions.
++ */
++
++#define XFIX_1_082392200 v0.4h[0]
++#define XFIX_1_414213562 v0.4h[1]
++#define XFIX_1_847759065 v0.4h[2]
++#define XFIX_2_613125930 v0.4h[3]
++
++.balign 16
++jsimd_idct_ifast_neon_consts:
++ .short (277 * 128 - 256 * 128) /* XFIX_1_082392200 */
++ .short (362 * 128 - 256 * 128) /* XFIX_1_414213562 */
++ .short (473 * 128 - 256 * 128) /* XFIX_1_847759065 */
++ .short (669 * 128 - 512 * 128) /* XFIX_2_613125930 */
++
++asm_function jsimd_idct_ifast_neon
++
++ DCT_TABLE .req x0
++ COEF_BLOCK .req x1
++ OUTPUT_BUF .req x2
++ OUTPUT_COL .req x3
++ TMP1 .req x0
++ TMP2 .req x1
++ TMP3 .req x2
++ TMP4 .req x22
++ TMP5 .req x23
++
++ /* Load and dequantize coefficients into NEON registers
++ * with the following allocation:
++ * 0 1 2 3 | 4 5 6 7
++ * ---------+--------
++ * 0 | d16 | d17 ( v8.8h )
++ * 1 | d18 | d19 ( v9.8h )
++ * 2 | d20 | d21 ( v10.8h )
++ * 3 | d22 | d23 ( v11.8h )
++ * 4 | d24 | d25 ( v12.8h )
++ * 5 | d26 | d27 ( v13.8h )
++ * 6 | d28 | d29 ( v14.8h )
++ * 7 | d30 | d31 ( v15.8h )
++ */
++ /* Save NEON registers used in fast IDCT */
++ sub sp, sp, #176
++ stp x22, x23, [sp], 16
++ adr x23, jsimd_idct_ifast_neon_consts
++ st1 {v0.8b - v3.8b}, [sp], 32
++ st1 {v4.8b - v7.8b}, [sp], 32
++ st1 {v8.8b - v11.8b}, [sp], 32
++ st1 {v12.8b - v15.8b}, [sp], 32
++ st1 {v16.8b - v19.8b}, [sp], 32
++ ld1 {v8.8h, v9.8h}, [COEF_BLOCK], 32
++ ld1 {v0.8h, v1.8h}, [DCT_TABLE], 32
++ ld1 {v10.8h, v11.8h}, [COEF_BLOCK], 32
++ mul v8.8h, v8.8h, v0.8h
++ ld1 {v2.8h, v3.8h}, [DCT_TABLE], 32
++ mul v9.8h, v9.8h, v1.8h
++ ld1 {v12.8h, v13.8h}, [COEF_BLOCK], 32
++ mul v10.8h, v10.8h, v2.8h
++ ld1 {v0.8h, v1.8h}, [DCT_TABLE], 32
++ mul v11.8h, v11.8h, v3.8h
++ ld1 {v14.8h, v15.8h}, [COEF_BLOCK], 32
++ mul v12.8h, v12.8h, v0.8h
++ ld1 {v2.8h, v3.8h}, [DCT_TABLE], 32
++ mul v14.8h, v14.8h, v2.8h
++ mul v13.8h, v13.8h, v1.8h
++ ld1 {v0.4h}, [x23] /* load constants */
++ mul v15.8h, v15.8h, v3.8h
++
++ /* 1-D IDCT, pass 1 */
++ sub v2.8h, v10.8h, v14.8h
++ add v14.8h, v10.8h, v14.8h
++ sub v1.8h, v11.8h, v13.8h
++ add v13.8h, v11.8h, v13.8h
++ sub v5.8h, v9.8h, v15.8h
++ add v15.8h, v9.8h, v15.8h
++ sqdmulh v4.8h, v2.8h, XFIX_1_414213562
++ sqdmulh v6.8h, v1.8h, XFIX_2_613125930
++ add v3.8h, v1.8h, v1.8h
++ sub v1.8h, v5.8h, v1.8h
++ add v10.8h, v2.8h, v4.8h
++ sqdmulh v4.8h, v1.8h, XFIX_1_847759065
++ sub v2.8h, v15.8h, v13.8h
++ add v3.8h, v3.8h, v6.8h
++ sqdmulh v6.8h, v2.8h, XFIX_1_414213562
++ add v1.8h, v1.8h, v4.8h
++ sqdmulh v4.8h, v5.8h, XFIX_1_082392200
++ sub v10.8h, v10.8h, v14.8h
++ add v2.8h, v2.8h, v6.8h
++ sub v6.8h, v8.8h, v12.8h
++ add v12.8h, v8.8h, v12.8h
++ add v9.8h, v5.8h, v4.8h
++ add v5.8h, v6.8h, v10.8h
++ sub v10.8h, v6.8h, v10.8h
++ add v6.8h, v15.8h, v13.8h
++ add v8.8h, v12.8h, v14.8h
++ sub v3.8h, v6.8h, v3.8h
++ sub v12.8h, v12.8h, v14.8h
++ sub v3.8h, v3.8h, v1.8h
++ sub v1.8h, v9.8h, v1.8h
++ add v2.8h, v3.8h, v2.8h
++ sub v15.8h, v8.8h, v6.8h
++ add v1.8h, v1.8h, v2.8h
++ add v8.8h, v8.8h, v6.8h
++ add v14.8h, v5.8h, v3.8h
++ sub v9.8h, v5.8h, v3.8h
++ sub v13.8h, v10.8h, v2.8h
++ add v10.8h, v10.8h, v2.8h
++ /* Transpose q8-q9 */
++ mov v18.16b, v8.16b
++ trn1 v8.8h, v8.8h, v9.8h
++ trn2 v9.8h, v18.8h, v9.8h
++ sub v11.8h, v12.8h, v1.8h
++ /* Transpose q14-q15 */
++ mov v18.16b, v14.16b
++ trn1 v14.8h, v14.8h, v15.8h
++ trn2 v15.8h, v18.8h, v15.8h
++ add v12.8h, v12.8h, v1.8h
++ /* Transpose q10-q11 */
++ mov v18.16b, v10.16b
++ trn1 v10.8h, v10.8h, v11.8h
++ trn2 v11.8h, v18.8h, v11.8h
++ /* Transpose q12-q13 */
++ mov v18.16b, v12.16b
++ trn1 v12.8h, v12.8h, v13.8h
++ trn2 v13.8h, v18.8h, v13.8h
++ /* Transpose q9-q11 */
++ mov v18.16b, v9.16b
++ trn1 v9.4s, v9.4s, v11.4s
++ trn2 v11.4s, v18.4s, v11.4s
++ /* Transpose q12-q14 */
++ mov v18.16b, v12.16b
++ trn1 v12.4s, v12.4s, v14.4s
++ trn2 v14.4s, v18.4s, v14.4s
++ /* Transpose q8-q10 */
++ mov v18.16b, v8.16b
++ trn1 v8.4s, v8.4s, v10.4s
++ trn2 v10.4s, v18.4s, v10.4s
++ /* Transpose q13-q15 */
++ mov v18.16b, v13.16b
++ trn1 v13.4s, v13.4s, v15.4s
++ trn2 v15.4s, v18.4s, v15.4s
++ /* vswp v14.4h, v10-MSB.4h */
++ umov x22, v14.d[0]
++ ins v14.2d[0], v10.2d[1]
++ ins v10.2d[1], x22
++ /* vswp v13.4h, v9MSB.4h */
++
++ umov x22, v13.d[0]
++ ins v13.2d[0], v9.2d[1]
++ ins v9.2d[1], x22
++ /* 1-D IDCT, pass 2 */
++ sub v2.8h, v10.8h, v14.8h
++ /* vswp v15.4h, v11MSB.4h */
++ umov x22, v15.d[0]
++ ins v15.2d[0], v11.2d[1]
++ ins v11.2d[1], x22
++ add v14.8h, v10.8h, v14.8h
++ /* vswp v12.4h, v8-MSB.4h */
++ umov x22, v12.d[0]
++ ins v12.2d[0], v8.2d[1]
++ ins v8.2d[1], x22
++ sub v1.8h, v11.8h, v13.8h
++ add v13.8h, v11.8h, v13.8h
++ sub v5.8h, v9.8h, v15.8h
++ add v15.8h, v9.8h, v15.8h
++ sqdmulh v4.8h, v2.8h, XFIX_1_414213562
++ sqdmulh v6.8h, v1.8h, XFIX_2_613125930
++ add v3.8h, v1.8h, v1.8h
++ sub v1.8h, v5.8h, v1.8h
++ add v10.8h, v2.8h, v4.8h
++ sqdmulh v4.8h, v1.8h, XFIX_1_847759065
++ sub v2.8h, v15.8h, v13.8h
++ add v3.8h, v3.8h, v6.8h
++ sqdmulh v6.8h, v2.8h, XFIX_1_414213562
++ add v1.8h, v1.8h, v4.8h
++ sqdmulh v4.8h, v5.8h, XFIX_1_082392200
++ sub v10.8h, v10.8h, v14.8h
++ add v2.8h, v2.8h, v6.8h
++ sub v6.8h, v8.8h, v12.8h
++ add v12.8h, v8.8h, v12.8h
++ add v9.8h, v5.8h, v4.8h
++ add v5.8h, v6.8h, v10.8h
++ sub v10.8h, v6.8h, v10.8h
++ add v6.8h, v15.8h, v13.8h
++ add v8.8h, v12.8h, v14.8h
++ sub v3.8h, v6.8h, v3.8h
++ sub v12.8h, v12.8h, v14.8h
++ sub v3.8h, v3.8h, v1.8h
++ sub v1.8h, v9.8h, v1.8h
++ add v2.8h, v3.8h, v2.8h
++ sub v15.8h, v8.8h, v6.8h
++ add v1.8h, v1.8h, v2.8h
++ add v8.8h, v8.8h, v6.8h
++ add v14.8h, v5.8h, v3.8h
++ sub v9.8h, v5.8h, v3.8h
++ sub v13.8h, v10.8h, v2.8h
++ add v10.8h, v10.8h, v2.8h
++ sub v11.8h, v12.8h, v1.8h
++ add v12.8h, v12.8h, v1.8h
++ /* Descale to 8-bit and range limit */
++ movi v0.16b, #0x80
++ sqshrn v8.8b, v8.8h, #5
++ sqshrn2 v8.16b, v9.8h, #5
++ sqshrn v9.8b, v10.8h, #5
++ sqshrn2 v9.16b, v11.8h, #5
++ sqshrn v10.8b, v12.8h, #5
++ sqshrn2 v10.16b, v13.8h, #5
++ sqshrn v11.8b, v14.8h, #5
++ sqshrn2 v11.16b, v15.8h, #5
++ add v8.16b, v8.16b, v0.16b
++ add v9.16b, v9.16b, v0.16b
++ add v10.16b, v10.16b, v0.16b
++ add v11.16b, v11.16b, v0.16b
++ /* Transpose the final 8-bit samples */
++ /* Transpose q8-q9 */
++ mov v18.16b, v8.16b
++ trn1 v8.8h, v8.8h, v9.8h
++ trn2 v9.8h, v18.8h, v9.8h
++ /* Transpose q10-q11 */
++ mov v18.16b, v10.16b
++ trn1 v10.8h, v10.8h, v11.8h
++ trn2 v11.8h, v18.8h, v11.8h
++ /* Transpose q8-q10 */
++ mov v18.16b, v8.16b
++ trn1 v8.4s, v8.4s, v10.4s
++ trn2 v10.4s, v18.4s, v10.4s
++ /* Transpose q9-q11 */
++ mov v18.16b, v9.16b
++ trn1 v9.4s, v9.4s, v11.4s
++ trn2 v11.4s, v18.4s, v11.4s
++ /* make copy */
++ ins v17.2d[0], v8.2d[1]
++ /* Transpose d16-d17-msb */
++ mov v18.16b, v8.16b
++ trn1 v8.8b, v8.8b, v17.8b
++ trn2 v17.8b, v18.8b, v17.8b
++ /* make copy */
++ ins v19.2d[0], v9.2d[1]
++ mov v18.16b, v9.16b
++ trn1 v9.8b, v9.8b, v19.8b
++ trn2 v19.8b, v18.8b, v19.8b
++ /* Store results to the output buffer */
++ ldp TMP1, TMP2, [OUTPUT_BUF], 16
++ add TMP1, TMP1, OUTPUT_COL
++ add TMP2, TMP2, OUTPUT_COL
++ st1 {v8.8b}, [TMP1]
++ st1 {v17.8b}, [TMP2]
++ ldp TMP1, TMP2, [OUTPUT_BUF], 16
++ add TMP1, TMP1, OUTPUT_COL
++ add TMP2, TMP2, OUTPUT_COL
++ st1 {v9.8b}, [TMP1]
++ /* make copy */
++ ins v7.2d[0], v10.2d[1]
++ mov v18.16b, v10.16b
++ trn1 v10.8b, v10.8b, v7.8b
++ trn2 v7.8b, v18.8b, v7.8b
++ st1 {v19.8b}, [TMP2]
++ ldp TMP1, TMP2, [OUTPUT_BUF], 16
++ ldp TMP4, TMP5, [OUTPUT_BUF], 16
++ add TMP1, TMP1, OUTPUT_COL
++ add TMP2, TMP2, OUTPUT_COL
++ add TMP4, TMP4, OUTPUT_COL
++ add TMP5, TMP5, OUTPUT_COL
++ st1 {v10.8b}, [TMP1]
++ /* make copy */
++ ins v16.2d[0], v11.2d[1]
++ mov v18.16b, v11.16b
++ trn1 v11.8b, v11.8b, v16.8b
++ trn2 v16.8b, v18.8b, v16.8b
++ st1 {v7.8b}, [TMP2]
++ st1 {v11.8b}, [TMP4]
++ st1 {v16.8b}, [TMP5]
++ sub sp, sp, #176
++ ldp x22, x23, [sp], 16
++ ld1 {v0.8b - v3.8b}, [sp], 32
++ ld1 {v4.8b - v7.8b}, [sp], 32
++ ld1 {v8.8b - v11.8b}, [sp], 32
++ ld1 {v12.8b - v15.8b}, [sp], 32
++ ld1 {v16.8b - v19.8b}, [sp], 32
++ blr x30
++
++ .unreq DCT_TABLE
++ .unreq COEF_BLOCK
++ .unreq OUTPUT_BUF
++ .unreq OUTPUT_COL
++ .unreq TMP1
++ .unreq TMP2
++ .unreq TMP3
++ .unreq TMP4
++
++
++/*****************************************************************************/
++
++/*
++ * jsimd_idct_4x4_neon
++ *
++ * This function contains inverse-DCT code for getting reduced-size
++ * 4x4 pixels output from an 8x8 DCT block. It uses the same calculations
++ * and produces exactly the same output as IJG's original 'jpeg_idct_4x4'
++ * function from jpeg-6b (jidctred.c).
++ *
++ * NOTE: jpeg-8 has an improved implementation of 4x4 inverse-DCT, which
++ * requires much less arithmetic operations and hence should be faster.
++ * The primary purpose of this particular NEON optimized function is
++ * bit exact compatibility with jpeg-6b.
++ *
++ * TODO: a bit better instructions scheduling can be achieved by expanding
++ * idct_helper/transpose_4x4 macros and reordering instructions,
++ * but readability will suffer somewhat.
++ */
++
++#define CONST_BITS 13
++
++#define FIX_0_211164243 (1730) /* FIX(0.211164243) */
++#define FIX_0_509795579 (4176) /* FIX(0.509795579) */
++#define FIX_0_601344887 (4926) /* FIX(0.601344887) */
++#define FIX_0_720959822 (5906) /* FIX(0.720959822) */
++#define FIX_0_765366865 (6270) /* FIX(0.765366865) */
++#define FIX_0_850430095 (6967) /* FIX(0.850430095) */
++#define FIX_0_899976223 (7373) /* FIX(0.899976223) */
++#define FIX_1_061594337 (8697) /* FIX(1.061594337) */
++#define FIX_1_272758580 (10426) /* FIX(1.272758580) */
++#define FIX_1_451774981 (11893) /* FIX(1.451774981) */
++#define FIX_1_847759065 (15137) /* FIX(1.847759065) */
++#define FIX_2_172734803 (17799) /* FIX(2.172734803) */
++#define FIX_2_562915447 (20995) /* FIX(2.562915447) */
++#define FIX_3_624509785 (29692) /* FIX(3.624509785) */
++
++.balign 16
++jsimd_idct_4x4_neon_consts:
++ .short FIX_1_847759065 /* v0.4h[0] */
++ .short -FIX_0_765366865 /* v0.4h[1] */
++ .short -FIX_0_211164243 /* v0.4h[2] */
++ .short FIX_1_451774981 /* v0.4h[3] */
++ .short -FIX_2_172734803 /* d1[0] */
++ .short FIX_1_061594337 /* d1[1] */
++ .short -FIX_0_509795579 /* d1[2] */
++ .short -FIX_0_601344887 /* d1[3] */
++ .short FIX_0_899976223 /* v2.4h[0] */
++ .short FIX_2_562915447 /* v2.4h[1] */
++ .short 1 << (CONST_BITS+1) /* v2.4h[2] */
++ .short 0 /* v2.4h[3] */
++
++.macro idct_helper x4, x6, x8, x10, x12, x14, x16, shift, y26, y27, y28, y29
++ smull v28.4s, \x4, v2.4h[2]
++ smlal v28.4s, \x8, v0.4h[0]
++ smlal v28.4s, \x14, v0.4h[1]
++
++ smull v26.4s, \x16, v1.4h[2]
++ smlal v26.4s, \x12, v1.4h[3]
++ smlal v26.4s, \x10, v2.4h[0]
++ smlal v26.4s, \x6, v2.4h[1]
++
++ smull v30.4s, \x4, v2.4h[2]
++ smlsl v30.4s, \x8, v0.4h[0]
++ smlsl v30.4s, \x14, v0.4h[1]
++
++ smull v24.4s, \x16, v0.4h[2]
++ smlal v24.4s, \x12, v0.4h[3]
++ smlal v24.4s, \x10, v1.4h[0]
++ smlal v24.4s, \x6, v1.4h[1]
++
++ add v20.4s, v28.4s, v26.4s
++ sub v28.4s, v28.4s, v26.4s
++
++.if \shift > 16
++ srshr v20.4s, v20.4s, #\shift
++ srshr v28.4s, v28.4s, #\shift
++ xtn \y26, v20.4s
++ xtn \y29, v28.4s
++.else
++ rshrn \y26, v20.4s, #\shift
++ rshrn \y29, v28.4s, #\shift
++.endif
++
++ add v20.4s, v30.4s, v24.4s
++ sub v30.4s, v30.4s, v24.4s
++
++.if \shift > 16
++ srshr v20.4s, v20.4s, #\shift
++ srshr v30.4s, v30.4s, #\shift
++ xtn \y27, v20.4s
++ xtn \y28, v30.4s
++.else
++ rshrn \y27, v20.4s, #\shift
++ rshrn \y28, v30.4s, #\shift
++.endif
++
++.endm
++
++asm_function jsimd_idct_4x4_neon
++
++ DCT_TABLE .req x0
++ COEF_BLOCK .req x1
++ OUTPUT_BUF .req x2
++ OUTPUT_COL .req x3
++ TMP1 .req x0
++ TMP2 .req x1
++ TMP3 .req x2
++ TMP4 .req x15
++
++ /* Save all used NEON registers */
++ sub sp, sp, 272
++ str x15, [sp], 16
++ /* Load constants (v3.4h is just used for padding) */
++ adr TMP4, jsimd_idct_4x4_neon_consts
++ st1 {v0.8b - v3.8b}, [sp], 32
++ st1 {v4.8b - v7.8b}, [sp], 32
++ st1 {v8.8b - v11.8b}, [sp], 32
++ st1 {v12.8b - v15.8b}, [sp], 32
++ st1 {v16.8b - v19.8b}, [sp], 32
++ st1 {v20.8b - v23.8b}, [sp], 32
++ st1 {v24.8b - v27.8b}, [sp], 32
++ st1 {v28.8b - v31.8b}, [sp], 32
++ ld1 {v0.4h, v1.4h, v2.4h, v3.4h}, [TMP4]
++
++ /* Load all COEF_BLOCK into NEON registers with the following allocation:
++ * 0 1 2 3 | 4 5 6 7
++ * ---------+--------
++ * 0 | v4.4h | v5.4h
++ * 1 | v6.4h | v7.4h
++ * 2 | v8.4h | v9.4h
++ * 3 | v10.4h | v11.4h
++ * 4 | - | -
++ * 5 | v12.4h | v13.4h
++ * 6 | v14.4h | v15.4h
++ * 7 | v16.4h | v17.4h
++ */
++ ld1 {v4.4h, v5.4h, v6.4h, v7.4h}, [COEF_BLOCK], 32
++ ld1 {v8.4h, v9.4h, v10.4h, v11.4h}, [COEF_BLOCK], 32
++ add COEF_BLOCK, COEF_BLOCK, #16
++ ld1 {v12.4h, v13.4h, v14.4h, v15.4h}, [COEF_BLOCK], 32
++ ld1 {v16.4h, v17.4h}, [COEF_BLOCK], 16
++ /* dequantize */
++ ld1 {v18.4h, v19.4h, v20.4h, v21.4h}, [DCT_TABLE], 32
++ mul v4.4h, v4.4h, v18.4h
++ mul v5.4h, v5.4h, v19.4h
++ ins v4.2d[1], v5.2d[0] /* 128 bit q4 */
++ ld1 {v22.4h, v23.4h, v24.4h, v25.4h}, [DCT_TABLE], 32
++ mul v6.4h, v6.4h, v20.4h
++ mul v7.4h, v7.4h, v21.4h
++ ins v6.2d[1], v7.2d[0] /* 128 bit q6 */
++ mul v8.4h, v8.4h, v22.4h
++ mul v9.4h, v9.4h, v23.4h
++ ins v8.2d[1], v9.2d[0] /* 128 bit q8 */
++ add DCT_TABLE, DCT_TABLE, #16
++ ld1 {v26.4h, v27.4h, v28.4h, v29.4h}, [DCT_TABLE], 32
++ mul v10.4h, v10.4h, v24.4h
++ mul v11.4h, v11.4h, v25.4h
++ ins v10.2d[1], v11.2d[0] /* 128 bit q10 */
++ mul v12.4h, v12.4h, v26.4h
++ mul v13.4h, v13.4h, v27.4h
++ ins v12.2d[1], v13.2d[0] /* 128 bit q12 */
++ ld1 {v30.4h, v31.4h}, [DCT_TABLE], 16
++ mul v14.4h, v14.4h, v28.4h
++ mul v15.4h, v15.4h, v29.4h
++ ins v14.2d[1], v15.2d[0] /* 128 bit q14 */
++ mul v16.4h, v16.4h, v30.4h
++ mul v17.4h, v17.4h, v31.4h
++ ins v16.2d[1], v17.2d[0] /* 128 bit q16 */
++
++ /* Pass 1 */
++ idct_helper v4.4h, v6.4h, v8.4h, v10.4h, v12.4h, v14.4h, v16.4h, 12, v4.4h, v6.4h, v8.4h, v10.4h
++ transpose_4x4 v4, v6, v8, v10, v3
++ ins v10.2d[1], v11.2d[0]
++ idct_helper v5.4h, v7.4h, v9.4h, v11.4h, v13.4h, v15.4h, v17.4h, 12, v5.4h, v7.4h, v9.4h, v11.4h
++ transpose_4x4 v5, v7, v9, v11, v3
++ ins v10.2d[1], v11.2d[0]
++ /* Pass 2 */
++ idct_helper v4.4h, v6.4h, v8.4h, v10.4h, v7.4h, v9.4h, v11.4h, 19, v26.4h, v27.4h, v28.4h, v29.4h
++ transpose_4x4 v26, v27, v28, v29, v3
++
++ /* Range limit */
++ movi v30.8h, #0x80
++ ins v26.2d[1], v27.2d[0]
++ ins v28.2d[1], v29.2d[0]
++ add v26.8h, v26.8h, v30.8h
++ add v28.8h, v28.8h, v30.8h
++ sqxtun v26.8b, v26.8h
++ sqxtun v27.8b, v28.8h
++
++ /* Store results to the output buffer */
++ ldp TMP1, TMP2, [OUTPUT_BUF], 16
++ ldp TMP3, TMP4, [OUTPUT_BUF]
++ add TMP1, TMP1, OUTPUT_COL
++ add TMP2, TMP2, OUTPUT_COL
++ add TMP3, TMP3, OUTPUT_COL
++ add TMP4, TMP4, OUTPUT_COL
++
++#if defined(__ARMEL__) && !RESPECT_STRICT_ALIGNMENT
++ /* We can use much less instructions on little endian systems if the
++ * OS kernel is not configured to trap unaligned memory accesses
++ */
++ st1 {v26.s}[0], [TMP1], 4
++ st1 {v27.s}[0], [TMP3], 4
++ st1 {v26.s}[1], [TMP2], 4
++ st1 {v27.s}[1], [TMP4], 4
++#else
++ st1 {v26.b}[0], [TMP1], 1
++ st1 {v27.b}[0], [TMP3], 1
++ st1 {v26.b}[1], [TMP1], 1
++ st1 {v27.b}[1], [TMP3], 1
++ st1 {v26.b}[2], [TMP1], 1
++ st1 {v27.b}[2], [TMP3], 1
++ st1 {v26.b}[3], [TMP1], 1
++ st1 {v27.b}[3], [TMP3], 1
++
++ st1 {v26.b}[4], [TMP2], 1
++ st1 {v27.b}[4], [TMP4], 1
++ st1 {v26.b}[5], [TMP2], 1
++ st1 {v27.b}[5], [TMP4], 1
++ st1 {v26.b}[6], [TMP2], 1
++ st1 {v27.b}[6], [TMP4], 1
++ st1 {v26.b}[7], [TMP2], 1
++ st1 {v27.b}[7], [TMP4], 1
++#endif
++
++ /* vpop {v8.4h - v15.4h} ;not available */
++ sub sp, sp, #272
++ ldr x15, [sp], 16
++ ld1 {v0.8b - v3.8b}, [sp], 32
++ ld1 {v4.8b - v7.8b}, [sp], 32
++ ld1 {v8.8b - v11.8b}, [sp], 32
++ ld1 {v12.8b - v15.8b}, [sp], 32
++ ld1 {v16.8b - v19.8b}, [sp], 32
++ ld1 {v20.8b - v23.8b}, [sp], 32
++ ld1 {v24.8b - v27.8b}, [sp], 32
++ ld1 {v28.8b - v31.8b}, [sp], 32
++ blr x30
++
++ .unreq DCT_TABLE
++ .unreq COEF_BLOCK
++ .unreq OUTPUT_BUF
++ .unreq OUTPUT_COL
++ .unreq TMP1
++ .unreq TMP2
++ .unreq TMP3
++ .unreq TMP4
++
++.purgem idct_helper
++
++
++/*****************************************************************************/
++
++/*
++ * jsimd_idct_2x2_neon
++ *
++ * This function contains inverse-DCT code for getting reduced-size
++ * 2x2 pixels output from an 8x8 DCT block. It uses the same calculations
++ * and produces exactly the same output as IJG's original 'jpeg_idct_2x2'
++ * function from jpeg-6b (jidctred.c).
++ *
++ * NOTE: jpeg-8 has an improved implementation of 2x2 inverse-DCT, which
++ * requires much less arithmetic operations and hence should be faster.
++ * The primary purpose of this particular NEON optimized function is
++ * bit exact compatibility with jpeg-6b.
++ */
++
++.balign 8
++jsimd_idct_2x2_neon_consts:
++ .short -FIX_0_720959822 /* v14[0] */
++ .short FIX_0_850430095 /* v14[1] */
++ .short -FIX_1_272758580 /* v14[2] */
++ .short FIX_3_624509785 /* v14[3] */
++
++.macro idct_helper x4, x6, x10, x12, x16, shift, y26, y27
++ sshll v15.4s, \x4, #15
++ smull v26.4s, \x6, v14.4h[3]
++ smlal v26.4s, \x10, v14.4h[2]
++ smlal v26.4s, \x12, v14.4h[1]
++ smlal v26.4s, \x16, v14.4h[0]
++
++ add v20.4s, v15.4s, v26.4s
++ sub v15.4s, v15.4s, v26.4s
++
++.if \shift > 16
++ srshr v20.4s, v20.4s, #\shift
++ srshr v15.4s, v15.4s, #\shift
++ xtn \y26, v20.4s
++ xtn \y27, v15.4s
++.else
++ rshrn \y26, v20.4s, #\shift
++ rshrn \y27, v15.4s, #\shift
++.endif
++
++.endm
++
++asm_function jsimd_idct_2x2_neon
++
++ DCT_TABLE .req x0
++ COEF_BLOCK .req x1
++ OUTPUT_BUF .req x2
++ OUTPUT_COL .req x3
++ TMP1 .req x0
++ TMP2 .req x15
++
++ /* vpush {v8.4h - v15.4h} ; not available */
++ sub sp, sp, 208
++ str x15, [sp], 16
++
++ /* Load constants */
++ adr TMP2, jsimd_idct_2x2_neon_consts
++ st1 {v4.8b - v7.8b}, [sp], 32
++ st1 {v8.8b - v11.8b}, [sp], 32
++ st1 {v12.8b - v15.8b}, [sp], 32
++ st1 {v16.8b - v19.8b}, [sp], 32
++ st1 {v21.8b - v22.8b}, [sp], 16
++ st1 {v24.8b - v27.8b}, [sp], 32
++ st1 {v30.8b - v31.8b}, [sp], 16
++ ld1 {v14.4h}, [TMP2]
++
++ /* Load all COEF_BLOCK into NEON registers with the following allocation:
++ * 0 1 2 3 | 4 5 6 7
++ * ---------+--------
++ * 0 | v4.4h | v5.4h
++ * 1 | v6.4h | v7.4h
++ * 2 | - | -
++ * 3 | v10.4h | v11.4h
++ * 4 | - | -
++ * 5 | v12.4h | v13.4h
++ * 6 | - | -
++ * 7 | v16.4h | v17.4h
++ */
++ ld1 {v4.4h, v5.4h, v6.4h, v7.4h}, [COEF_BLOCK], 32
++ add COEF_BLOCK, COEF_BLOCK, #16
++ ld1 {v10.4h, v11.4h}, [COEF_BLOCK], 16
++ add COEF_BLOCK, COEF_BLOCK, #16
++ ld1 {v12.4h, v13.4h}, [COEF_BLOCK], 16
++ add COEF_BLOCK, COEF_BLOCK, #16
++ ld1 {v16.4h, v17.4h}, [COEF_BLOCK], 16
++ /* Dequantize */
++ ld1 {v18.4h, v19.4h, v20.4h, v21.4h}, [DCT_TABLE], 32
++ mul v4.4h, v4.4h, v18.4h
++ mul v5.4h, v5.4h, v19.4h
++ ins v4.2d[1], v5.2d[0]
++ mul v6.4h, v6.4h, v20.4h
++ mul v7.4h, v7.4h, v21.4h
++ ins v6.2d[1], v7.2d[0]
++ add DCT_TABLE, DCT_TABLE, #16
++ ld1 {v24.4h, v25.4h}, [DCT_TABLE], 16
++ mul v10.4h, v10.4h, v24.4h
++ mul v11.4h, v11.4h, v25.4h
++ ins v10.2d[1], v11.2d[0]
++ add DCT_TABLE, DCT_TABLE, #16
++ ld1 {v26.4h, v27.4h}, [DCT_TABLE], 16
++ mul v12.4h, v12.4h, v26.4h
++ mul v13.4h, v13.4h, v27.4h
++ ins v12.2d[1], v13.2d[0]
++ add DCT_TABLE, DCT_TABLE, #16
++ ld1 {v30.4h, v31.4h}, [DCT_TABLE], 16
++ mul v16.4h, v16.4h, v30.4h
++ mul v17.4h, v17.4h, v31.4h
++ ins v16.2d[1], v17.2d[0]
++
++ /* Pass 1 */
++#if 0
++ idct_helper v4.4h, v6.4h, v10.4h, v12.4h, v16.4h, 13, v4.4h, v6.4h
++ transpose_4x4 v4.4h, v6.4h, v8.4h, v10.4h
++ idct_helper v5.4h, v7.4h, v11.4h, v13.4h, v17.4h, 13, v5.4h, v7.4h
++ transpose_4x4 v5.4h, v7.4h, v9.4h, v11.4h
++#else
++ smull v26.4s, v6.4h, v14.4h[3]
++ smlal v26.4s, v10.4h, v14.4h[2]
++ smlal v26.4s, v12.4h, v14.4h[1]
++ smlal v26.4s, v16.4h, v14.4h[0]
++ smull v24.4s, v7.4h, v14.4h[3]
++ smlal v24.4s, v11.4h, v14.4h[2]
++ smlal v24.4s, v13.4h, v14.4h[1]
++ smlal v24.4s, v17.4h, v14.4h[0]
++ sshll v15.4s, v4.4h, #15
++ sshll v30.4s, v5.4h, #15
++ add v20.4s, v15.4s, v26.4s
++ sub v15.4s, v15.4s, v26.4s
++ rshrn v4.4h, v20.4s, #13
++ rshrn v6.4h, v15.4s, #13
++ add v20.4s, v30.4s, v24.4s
++ sub v15.4s, v30.4s, v24.4s
++ rshrn v5.4h, v20.4s, #13
++ rshrn v7.4h, v15.4s, #13
++ ins v4.2d[1], v5.2d[0]
++ ins v6.2d[1], v7.2d[0]
++ transpose v4, v6, v3, .16b, .8h
++ transpose v6, v10, v3, .16b, .4s
++ ins v11.2d[0], v10.2d[1]
++ ins v7.2d[0], v6.2d[1]
++#endif
++
++ /* Pass 2 */
++ idct_helper v4.4h, v6.4h, v10.4h, v7.4h, v11.4h, 20, v26.4h, v27.4h
++
++ /* Range limit */
++ movi v30.8h, #0x80
++ ins v26.2d[1], v27.2d[0]
++ add v26.8h, v26.8h, v30.8h
++ sqxtun v30.8b, v26.8h
++ ins v26.2d[0], v30.2d[0]
++ sqxtun v27.8b, v26.8h
++
++ /* Store results to the output buffer */
++ ldp TMP1, TMP2, [OUTPUT_BUF]
++ add TMP1, TMP1, OUTPUT_COL
++ add TMP2, TMP2, OUTPUT_COL
++
++ st1 {v26.b}[0], [TMP1], 1
++ st1 {v27.b}[4], [TMP1], 1
++ st1 {v26.b}[1], [TMP2], 1
++ st1 {v27.b}[5], [TMP2], 1
++
++ sub sp, sp, #208
++ ldr x15, [sp], 16
++ ld1 {v4.8b - v7.8b}, [sp], 32
++ ld1 {v8.8b - v11.8b}, [sp], 32
++ ld1 {v12.8b - v15.8b}, [sp], 32
++ ld1 {v16.8b - v19.8b}, [sp], 32
++ ld1 {v21.8b - v22.8b}, [sp], 16
++ ld1 {v24.8b - v27.8b}, [sp], 32
++ ld1 {v30.8b - v31.8b}, [sp], 16
++ blr x30
++
++ .unreq DCT_TABLE
++ .unreq COEF_BLOCK
++ .unreq OUTPUT_BUF
++ .unreq OUTPUT_COL
++ .unreq TMP1
++ .unreq TMP2
++
++.purgem idct_helper
++
++
++/*****************************************************************************/
++
++/*
++ * jsimd_ycc_extrgb_convert_neon
++ * jsimd_ycc_extbgr_convert_neon
++ * jsimd_ycc_extrgbx_convert_neon
++ * jsimd_ycc_extbgrx_convert_neon
++ * jsimd_ycc_extxbgr_convert_neon
++ * jsimd_ycc_extxrgb_convert_neon
++ *
++ * Colorspace conversion YCbCr -> RGB
++ */
++
++
++.macro do_load size
++ .if \size == 8
++ ld1 {v4.8b}, [U], 8
++ ld1 {v5.8b}, [V], 8
++ ld1 {v0.8b}, [Y], 8
++ prfm PLDL1KEEP, [U, #64]
++ prfm PLDL1KEEP, [V, #64]
++ prfm PLDL1KEEP, [Y, #64]
++ .elseif \size == 4
++ ld1 {v4.b}[0], [U], 1
++ ld1 {v4.b}[1], [U], 1
++ ld1 {v4.b}[2], [U], 1
++ ld1 {v4.b}[3], [U], 1
++ ld1 {v5.b}[0], [V], 1
++ ld1 {v5.b}[1], [V], 1
++ ld1 {v5.b}[2], [V], 1
++ ld1 {v5.b}[3], [V], 1
++ ld1 {v0.b}[0], [Y], 1
++ ld1 {v0.b}[1], [Y], 1
++ ld1 {v0.b}[2], [Y], 1
++ ld1 {v0.b}[3], [Y], 1
++ .elseif \size == 2
++ ld1 {v4.b}[4], [U], 1
++ ld1 {v4.b}[5], [U], 1
++ ld1 {v5.b}[4], [V], 1
++ ld1 {v5.b}[5], [V], 1
++ ld1 {v0.b}[4], [Y], 1
++ ld1 {v0.b}[5], [Y], 1
++ .elseif \size == 1
++ ld1 {v4.b}[6], [U], 1
++ ld1 {v5.b}[6], [V], 1
++ ld1 {v0.b}[6], [Y], 1
++ .else
++ .error unsupported macroblock size
++ .endif
++.endm
++
++.macro do_store bpp, size
++ .if \bpp == 24
++ .if \size == 8
++ st3 {v10.8b, v11.8b, v12.8b}, [RGB], 24
++ .elseif \size == 4
++ st3 {v10.b, v11.b, v12.b}[0], [RGB], 3
++ st3 {v10.b, v11.b, v12.b}[1], [RGB], 3
++ st3 {v10.b, v11.b, v12.b}[2], [RGB], 3
++ st3 {v10.b, v11.b, v12.b}[3], [RGB], 3
++ .elseif \size == 2
++ st3 {v10.b, v11.b, v12.b}[4], [RGB], 3
++ st3 {v10.b, v11.b, v12.b}[5], [RGB], 3
++ .elseif \size == 1
++ st3 {v10.b, v11.b, v12.b}[6], [RGB], 3
++ .else
++ .error unsupported macroblock size
++ .endif
++ .elseif \bpp == 32
++ .if \size == 8
++ st4 {v10.8b, v11.8b, v12.8b, v13.8b}, [RGB], 32
++ .elseif \size == 4
++ st4 {v10.b, v11.b, v12.b, v13.b}[0], [RGB], 4
++ st4 {v10.b, v11.b, v12.b, v13.b}[1], [RGB], 4
++ st4 {v10.b, v11.b, v12.b, v13.b}[2], [RGB], 4
++ st4 {v10.b, v11.b, v12.b, v13.b}[3], [RGB], 4
++ .elseif \size == 2
++ st4 {v10.b, v11.b, v12.b, v13.b}[4], [RGB], 4
++ st4 {v10.b, v11.b, v12.b, v13.b}[5], [RGB], 4
++ .elseif \size == 1
++ st4 {v10.b, v11.b, v12.b, v13.b}[6], [RGB], 4
++ .else
++ .error unsupported macroblock size
++ .endif
++ .elseif \bpp==16
++ .if \size == 8
++ st1 {v25.8h}, [RGB],16
++ .elseif \size == 4
++ st1 {v25.4h}, [RGB],8
++ .elseif \size == 2
++ st1 {v25.h}[4], [RGB],2
++ st1 {v25.h}[5], [RGB],2
++ .elseif \size == 1
++ st1 {v25.h}[6], [RGB],2
++ .else
++ .error unsupported macroblock size
++ .endif
++ .else
++ .error unsupported bpp
++ .endif
++.endm
++
++.macro generate_jsimd_ycc_rgb_convert_neon colorid, bpp, r_offs, rsize, g_offs, gsize, b_offs, bsize, defsize
++
++/*
++ * 2-stage pipelined YCbCr->RGB conversion
++ */
++
++.macro do_yuv_to_rgb_stage1
++ uaddw v6.8h, v2.8h, v4.8b /* q3 = u - 128 */
++ uaddw v8.8h, v2.8h, v5.8b /* q2 = v - 128 */
++ smull v20.4s, v6.4h, v1.4h[1] /* multiply by -11277 */
++ smlal v20.4s, v8.4h, v1.4h[2] /* multiply by -23401 */
++ smull2 v22.4s, v6.8h, v1.4h[1] /* multiply by -11277 */
++ smlal2 v22.4s, v8.8h, v1.4h[2] /* multiply by -23401 */
++ smull v24.4s, v8.4h, v1.4h[0] /* multiply by 22971 */
++ smull2 v26.4s, v8.8h, v1.4h[0] /* multiply by 22971 */
++ smull v28.4s, v6.4h, v1.4h[3] /* multiply by 29033 */
++ smull2 v30.4s, v6.8h, v1.4h[3] /* multiply by 29033 */
++.endm
++
++.macro do_yuv_to_rgb_stage2
++ rshrn v20.4h, v20.4s, #15
++ rshrn2 v20.8h, v22.4s, #15
++ rshrn v24.4h, v24.4s, #14
++ rshrn2 v24.8h, v26.4s, #14
++ rshrn v28.4h, v28.4s, #14
++ rshrn2 v28.8h, v30.4s, #14
++ uaddw v20.8h, v20.8h, v0.8b
++ uaddw v24.8h, v24.8h, v0.8b
++ uaddw v28.8h, v28.8h, v0.8b
++.if \bpp != 16
++ sqxtun v1\g_offs\defsize, v20.8h
++ sqxtun v1\r_offs\defsize, v24.8h
++ sqxtun v1\b_offs\defsize, v28.8h
++.else
++ sqshlu v21.8h, v20.8h, #8
++ sqshlu v25.8h, v24.8h, #8
++ sqshlu v29.8h, v28.8h, #8
++ sri v25.8h, v21.8h, #5
++ sri v25.8h, v29.8h, #11
++.endif
++
++.endm
++
++.macro do_yuv_to_rgb_stage2_store_load_stage1
++ rshrn v20.4h, v20.4s, #15
++ rshrn v24.4h, v24.4s, #14
++ rshrn v28.4h, v28.4s, #14
++ ld1 {v4.8b}, [U], 8
++ rshrn2 v20.8h, v22.4s, #15
++ rshrn2 v24.8h, v26.4s, #14
++ rshrn2 v28.8h, v30.4s, #14
++ ld1 {v5.8b}, [V], 8
++ uaddw v20.8h, v20.8h, v0.8b
++ uaddw v24.8h, v24.8h, v0.8b
++ uaddw v28.8h, v28.8h, v0.8b
++.if \bpp != 16 /**************** rgb24/rgb32 *********************************/
++ sqxtun v1\g_offs\defsize, v20.8h
++ ld1 {v0.8b}, [Y], 8
++ sqxtun v1\r_offs\defsize, v24.8h
++ prfm PLDL1KEEP, [U, #64]
++ prfm PLDL1KEEP, [V, #64]
++ prfm PLDL1KEEP, [Y, #64]
++ sqxtun v1\b_offs\defsize, v28.8h
++ uaddw v6.8h, v2.8h, v4.8b /* v6.16b = u - 128 */
++ uaddw v8.8h, v2.8h, v5.8b /* q2 = v - 128 */
++ smull v20.4s, v6.4h, v1.4h[1] /* multiply by -11277 */
++ smlal v20.4s, v8.4h, v1.4h[2] /* multiply by -23401 */
++ smull2 v22.4s, v6.8h, v1.4h[1] /* multiply by -11277 */
++ smlal2 v22.4s, v8.8h, v1.4h[2] /* multiply by -23401 */
++ smull v24.4s, v8.4h, v1.4h[0] /* multiply by 22971 */
++ smull2 v26.4s, v8.8h, v1.4h[0] /* multiply by 22971 */
++.else /**************************** rgb565 ***********************************/
++ sqshlu v21.8h, v20.8h, #8
++ sqshlu v25.8h, v24.8h, #8
++ sqshlu v29.8h, v28.8h, #8
++ uaddw v6.8h, v2.8h, v4.8b /* v6.16b = u - 128 */
++ uaddw v8.8h, v2.8h, v5.8b /* q2 = v - 128 */
++ ld1 {v0.8b}, [Y], 8
++ smull v20.4s, v6.4h, v1.4h[1] /* multiply by -11277 */
++ smlal v20.4s, v8.4h, v1.4h[2] /* multiply by -23401 */
++ smull2 v22.4s, v6.8h, v1.4h[1] /* multiply by -11277 */
++ smlal2 v22.4s, v8.8h, v1.4h[2] /* multiply by -23401 */
++ sri v25.8h, v21.8h, #5
++ smull v24.4s, v8.4h, v1.4h[0] /* multiply by 22971 */
++ smull2 v26.4s, v8.8h, v1.4h[0] /* multiply by 22971 */
++ prfm PLDL1KEEP, [U, #64]
++ prfm PLDL1KEEP, [V, #64]
++ prfm PLDL1KEEP, [Y, #64]
++ sri v25.8h, v29.8h, #11
++.endif
++ do_store \bpp, 8
++ smull v28.4s, v6.4h, v1.4h[3] /* multiply by 29033 */
++ smull2 v30.4s, v6.8h, v1.4h[3] /* multiply by 29033 */
++.endm
++
++.macro do_yuv_to_rgb
++ do_yuv_to_rgb_stage1
++ do_yuv_to_rgb_stage2
++.endm
++
++/* Apple gas crashes on adrl, work around that by using adr.
++ * But this requires a copy of these constants for each function.
++ */
++
++.balign 16
++jsimd_ycc_\colorid\()_neon_consts:
++ .short 0, 0, 0, 0
++ .short 22971, -11277, -23401, 29033
++ .short -128, -128, -128, -128
++ .short -128, -128, -128, -128
++
++asm_function jsimd_ycc_\colorid\()_convert_neon
++ OUTPUT_WIDTH .req x0
++ INPUT_BUF .req x1
++ INPUT_ROW .req x2
++ OUTPUT_BUF .req x3
++ NUM_ROWS .req x4
++
++ INPUT_BUF0 .req x5
++ INPUT_BUF1 .req x6
++ INPUT_BUF2 .req INPUT_BUF
++
++ RGB .req x7
++ Y .req x8
++ U .req x9
++ V .req x10
++ N .req x15
++
++ sub sp, sp, 336
++ str x15, [sp], 16
++ /* Load constants to d1, d2, d3 (v0.4h is just used for padding) */
++ adr x15, jsimd_ycc_\colorid\()_neon_consts
++ /* Save NEON registers */
++ st1 {v0.8b - v3.8b}, [sp], 32
++ st1 {v4.8b - v7.8b}, [sp], 32
++ st1 {v8.8b - v11.8b}, [sp], 32
++ st1 {v12.8b - v15.8b}, [sp], 32
++ st1 {v16.8b - v19.8b}, [sp], 32
++ st1 {v20.8b - v23.8b}, [sp], 32
++ st1 {v24.8b - v27.8b}, [sp], 32
++ st1 {v28.8b - v31.8b}, [sp], 32
++ ld1 {v0.4h, v1.4h}, [x15], 16
++ ld1 {v2.8h}, [x15]
++
++ /* Save ARM registers and handle input arguments */
++ /* push {x4, x5, x6, x7, x8, x9, x10, x30} */
++ stp x4, x5, [sp], 16
++ stp x6, x7, [sp], 16
++ stp x8, x9, [sp], 16
++ stp x10, x30, [sp], 16
++ ldr INPUT_BUF0, [INPUT_BUF]
++ ldr INPUT_BUF1, [INPUT_BUF, 8]
++ ldr INPUT_BUF2, [INPUT_BUF, 16]
++ .unreq INPUT_BUF
++
++ /* Initially set v10, v11.4h, v12.8b, d13 to 0xFF */
++ movi v10.16b, #255
++ movi v13.16b, #255
++
++ /* Outer loop over scanlines */
++ cmp NUM_ROWS, #1
++ blt 9f
++0:
++ lsl x16, INPUT_ROW, #3
++ ldr Y, [INPUT_BUF0, x16]
++ ldr U, [INPUT_BUF1, x16]
++ mov N, OUTPUT_WIDTH
++ ldr V, [INPUT_BUF2, x16]
++ add INPUT_ROW, INPUT_ROW, #1
++ ldr RGB, [OUTPUT_BUF], #8
++
++ /* Inner loop over pixels */
++ subs N, N, #8
++ blt 3f
++ do_load 8
++ do_yuv_to_rgb_stage1
++ subs N, N, #8
++ blt 2f
++1:
++ do_yuv_to_rgb_stage2_store_load_stage1
++ subs N, N, #8
++ bge 1b
++2:
++ do_yuv_to_rgb_stage2
++ do_store \bpp, 8
++ tst N, #7
++ beq 8f
++3:
++ tst N, #4
++ beq 3f
++ do_load 4
++3:
++ tst N, #2
++ beq 4f
++ do_load 2
++4:
++ tst N, #1
++ beq 5f
++ do_load 1
++5:
++ do_yuv_to_rgb
++ tst N, #4
++ beq 6f
++ do_store \bpp, 4
++6:
++ tst N, #2
++ beq 7f
++ do_store \bpp, 2
++7:
++ tst N, #1
++ beq 8f
++ do_store \bpp, 1
++8:
++ subs NUM_ROWS, NUM_ROWS, #1
++ bgt 0b
++9:
++ /* Restore all registers and return */
++ sub sp, sp, #336
++ ldr x15, [sp], 16
++ ld1 {v0.8b - v3.8b}, [sp], 32
++ ld1 {v4.8b - v7.8b}, [sp], 32
++ ld1 {v8.8b - v11.8b}, [sp], 32
++ ld1 {v12.8b - v15.8b}, [sp], 32
++ ld1 {v16.8b - v19.8b}, [sp], 32
++ ld1 {v20.8b - v23.8b}, [sp], 32
++ ld1 {v24.8b - v27.8b}, [sp], 32
++ ld1 {v28.8b - v31.8b}, [sp], 32
++ /* pop {r4, r5, r6, r7, r8, r9, r10, pc} */
++ ldp x4, x5, [sp], 16
++ ldp x6, x7, [sp], 16
++ ldp x8, x9, [sp], 16
++ ldp x10, x30, [sp], 16
++ br x30
++ .unreq OUTPUT_WIDTH
++ .unreq INPUT_ROW
++ .unreq OUTPUT_BUF
++ .unreq NUM_ROWS
++ .unreq INPUT_BUF0
++ .unreq INPUT_BUF1
++ .unreq INPUT_BUF2
++ .unreq RGB
++ .unreq Y
++ .unreq U
++ .unreq V
++ .unreq N
++
++.purgem do_yuv_to_rgb
++.purgem do_yuv_to_rgb_stage1
++.purgem do_yuv_to_rgb_stage2
++.purgem do_yuv_to_rgb_stage2_store_load_stage1
++.endm
++
++/*--------------------------------- id ----- bpp R rsize G gsize B bsize defsize */
++generate_jsimd_ycc_rgb_convert_neon extrgb, 24, 0, .4h, 1, .4h, 2, .4h, .8b
++generate_jsimd_ycc_rgb_convert_neon extbgr, 24, 2, .4h, 1, .4h, 0, .4h, .8b
++generate_jsimd_ycc_rgb_convert_neon extrgbx, 32, 0, .4h, 1, .4h, 2, .4h, .8b
++generate_jsimd_ycc_rgb_convert_neon extbgrx, 32, 2, .4h, 1, .4h, 0, .4h, .8b
++generate_jsimd_ycc_rgb_convert_neon extxbgr, 32, 3, .4h, 2, .4h, 1, .4h, .8b
++generate_jsimd_ycc_rgb_convert_neon extxrgb, 32, 1, .4h, 2, .4h, 3, .4h, .8b
++generate_jsimd_ycc_rgb_convert_neon rgb565, 16, 0, .4h, 0, .4h, 0, .4h, .8b
++.purgem do_load
++.purgem do_store
}],
[ 'target_arch=="arm64"', {
'sources': [
- 'jsimd_none.c',
+ 'simd/jsimd_arm64.c',
+ 'simd/jsimd_arm64_neon.S',
],
}],
[ 'target_arch=="mipsel"', {
],
},
}],
- [ 'OS=="mac"', {
+ [ 'OS=="mac" or OS=="ios"', {
'dependencies': [
'../yasm/yasm.gyp:yasm#host',
],
],
},
}],
- [ 'OS=="linux" or OS=="freebsd" or (OS=="android" and target_arch!="arm")', {
+ [ 'OS=="linux" or OS=="freebsd" or (OS=="android" and (target_arch=="ia32" or target_arch=="x64"))', {
'conditions': [
[ 'use_system_yasm==0', {
'dependencies': [
'rule_name': 'assemble',
'extension': 'asm',
'conditions': [
- [ 'target_arch!="arm"', {
+ [ 'target_arch=="ia32" or target_arch=="x64"', {
'inputs': [ '<(yasm_path)', ],
'outputs': [
'<(shared_generated_dir)/<(RULE_INPUT_ROOT).<(object_suffix)',
--- /dev/null
+/*
+ * jsimd_arm64.c
+ *
+ * Copyright 2009 Pierre Ossman <ossman@cendio.se> for Cendio AB
+ * Copyright 2009-2011, 2013-2014 D. R. Commander
+ *
+ * Based on the x86 SIMD extension for IJG JPEG library,
+ * Copyright (C) 1999-2006, MIYASAKA Masaru.
+ * For conditions of distribution and use, see copyright notice in jsimdext.inc
+ *
+ * This file contains the interface between the "normal" portions
+ * of the library and the SIMD implementations when running on a
+ * 64-bit ARM architecture.
+ */
+
+#define JPEG_INTERNALS
+#include "../jinclude.h"
+#include "../jpeglib.h"
+#include "../jsimd.h"
+#include "../jdct.h"
+#include "../jsimddct.h"
+#include "jsimd.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+static unsigned int simd_support = ~0;
+
+/*
+ * Check what SIMD accelerations are supported.
+ *
+ * FIXME: This code is racy under a multi-threaded environment.
+ */
+
+/*
+ * ARMv8 architectures support NEON extensions by default.
+ * It is no longer optional as it was with ARMv7.
+ */
+
+
+LOCAL(void)
+init_simd (void)
+{
+ char *env = NULL;
+
+ if (simd_support != ~0U)
+ return;
+
+ simd_support = 0;
+
+ simd_support |= JSIMD_ARM_NEON;
+
+ /* Force different settings through environment variables */
+ env = getenv("JSIMD_FORCENEON");
+ if ((env != NULL) && (strcmp(env, "1") == 0))
+ simd_support &= JSIMD_ARM_NEON;
+ env = getenv("JSIMD_FORCENONE");
+ if ((env != NULL) && (strcmp(env, "1") == 0))
+ simd_support = 0;
+}
+
+GLOBAL(int)
+jsimd_can_rgb_ycc (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_rgb_gray (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_ycc_rgb (void)
+{
+ init_simd();
+
+ /* The code is optimised for these values only */
+ if (BITS_IN_JSAMPLE != 8)
+ return 0;
+ if (sizeof(JDIMENSION) != 4)
+ return 0;
+ if ((RGB_PIXELSIZE != 3) && (RGB_PIXELSIZE != 4))
+ return 0;
+
+ if (simd_support & JSIMD_ARM_NEON)
+ return 1;
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_ycc_rgb565 (void)
+{
+ init_simd();
+
+ /* The code is optimised for these values only */
+ if (BITS_IN_JSAMPLE != 8)
+ return 0;
+ if (sizeof(JDIMENSION) != 4)
+ return 0;
+
+ if (simd_support & JSIMD_ARM_NEON)
+ return 1;
+
+ return 0;
+}
+
+GLOBAL(void)
+jsimd_rgb_ycc_convert (j_compress_ptr cinfo,
+ JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+ JDIMENSION output_row, int num_rows)
+{
+}
+
+GLOBAL(void)
+jsimd_rgb_gray_convert (j_compress_ptr cinfo,
+ JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+ JDIMENSION output_row, int num_rows)
+{
+}
+
+GLOBAL(void)
+jsimd_ycc_rgb_convert (j_decompress_ptr cinfo,
+ JSAMPIMAGE input_buf, JDIMENSION input_row,
+ JSAMPARRAY output_buf, int num_rows)
+{
+ void (*neonfct)(JDIMENSION, JSAMPIMAGE, JDIMENSION, JSAMPARRAY, int);
+
+ switch(cinfo->out_color_space) {
+ case JCS_EXT_RGB:
+ neonfct=jsimd_ycc_extrgb_convert_neon;
+ break;
+ case JCS_EXT_RGBX:
+ case JCS_EXT_RGBA:
+ neonfct=jsimd_ycc_extrgbx_convert_neon;
+ break;
+ case JCS_EXT_BGR:
+ neonfct=jsimd_ycc_extbgr_convert_neon;
+ break;
+ case JCS_EXT_BGRX:
+ case JCS_EXT_BGRA:
+ neonfct=jsimd_ycc_extbgrx_convert_neon;
+ break;
+ case JCS_EXT_XBGR:
+ case JCS_EXT_ABGR:
+ neonfct=jsimd_ycc_extxbgr_convert_neon;
+ break;
+ case JCS_EXT_XRGB:
+ case JCS_EXT_ARGB:
+ neonfct=jsimd_ycc_extxrgb_convert_neon;
+ break;
+ default:
+ neonfct=jsimd_ycc_extrgb_convert_neon;
+ break;
+ }
+
+ if (simd_support & JSIMD_ARM_NEON)
+ neonfct(cinfo->output_width, input_buf, input_row, output_buf, num_rows);
+}
+
+GLOBAL(void)
+jsimd_ycc_rgb565_convert (j_decompress_ptr cinfo,
+ JSAMPIMAGE input_buf, JDIMENSION input_row,
+ JSAMPARRAY output_buf, int num_rows)
+{
+ if (simd_support & JSIMD_ARM_NEON)
+ jsimd_ycc_rgb565_convert_neon(cinfo->output_width, input_buf, input_row,
+ output_buf, num_rows);
+}
+
+GLOBAL(int)
+jsimd_can_h2v2_downsample (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_h2v1_downsample (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(void)
+jsimd_h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
+ JSAMPARRAY input_data, JSAMPARRAY output_data)
+{
+}
+
+GLOBAL(void)
+jsimd_h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr,
+ JSAMPARRAY input_data, JSAMPARRAY output_data)
+{
+}
+
+GLOBAL(int)
+jsimd_can_h2v2_upsample (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_h2v1_upsample (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(void)
+jsimd_h2v2_upsample (j_decompress_ptr cinfo,
+ jpeg_component_info * compptr,
+ JSAMPARRAY input_data,
+ JSAMPARRAY * output_data_ptr)
+{
+}
+
+GLOBAL(void)
+jsimd_h2v1_upsample (j_decompress_ptr cinfo,
+ jpeg_component_info * compptr,
+ JSAMPARRAY input_data,
+ JSAMPARRAY * output_data_ptr)
+{
+}
+
+GLOBAL(int)
+jsimd_can_h2v2_fancy_upsample (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_h2v1_fancy_upsample (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(void)
+jsimd_h2v2_fancy_upsample (j_decompress_ptr cinfo,
+ jpeg_component_info * compptr,
+ JSAMPARRAY input_data,
+ JSAMPARRAY * output_data_ptr)
+{
+}
+
+GLOBAL(void)
+jsimd_h2v1_fancy_upsample (j_decompress_ptr cinfo,
+ jpeg_component_info * compptr,
+ JSAMPARRAY input_data,
+ JSAMPARRAY * output_data_ptr)
+{
+}
+
+GLOBAL(int)
+jsimd_can_h2v2_merged_upsample (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_h2v1_merged_upsample (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(void)
+jsimd_h2v2_merged_upsample (j_decompress_ptr cinfo,
+ JSAMPIMAGE input_buf,
+ JDIMENSION in_row_group_ctr,
+ JSAMPARRAY output_buf)
+{
+}
+
+GLOBAL(void)
+jsimd_h2v1_merged_upsample (j_decompress_ptr cinfo,
+ JSAMPIMAGE input_buf,
+ JDIMENSION in_row_group_ctr,
+ JSAMPARRAY output_buf)
+{
+}
+
+GLOBAL(int)
+jsimd_can_convsamp (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_convsamp_float (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(void)
+jsimd_convsamp (JSAMPARRAY sample_data, JDIMENSION start_col,
+ DCTELEM * workspace)
+{
+}
+
+GLOBAL(void)
+jsimd_convsamp_float (JSAMPARRAY sample_data, JDIMENSION start_col,
+ FAST_FLOAT * workspace)
+{
+}
+
+GLOBAL(int)
+jsimd_can_fdct_islow (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_fdct_ifast (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_fdct_float (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(void)
+jsimd_fdct_islow (DCTELEM * data)
+{
+}
+
+GLOBAL(void)
+jsimd_fdct_ifast (DCTELEM * data)
+{
+}
+
+GLOBAL(void)
+jsimd_fdct_float (FAST_FLOAT * data)
+{
+}
+
+GLOBAL(int)
+jsimd_can_quantize (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_quantize_float (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(void)
+jsimd_quantize (JCOEFPTR coef_block, DCTELEM * divisors,
+ DCTELEM * workspace)
+{
+}
+
+GLOBAL(void)
+jsimd_quantize_float (JCOEFPTR coef_block, FAST_FLOAT * divisors,
+ FAST_FLOAT * workspace)
+{
+}
+
+GLOBAL(int)
+jsimd_can_idct_2x2 (void)
+{
+ init_simd();
+
+ /* The code is optimised for these values only */
+ if (DCTSIZE != 8)
+ return 0;
+ if (sizeof(JCOEF) != 2)
+ return 0;
+ if (BITS_IN_JSAMPLE != 8)
+ return 0;
+ if (sizeof(JDIMENSION) != 4)
+ return 0;
+ if (sizeof(ISLOW_MULT_TYPE) != 2)
+ return 0;
+
+ if (simd_support & JSIMD_ARM_NEON)
+ return 1;
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_idct_4x4 (void)
+{
+ init_simd();
+
+ /* The code is optimised for these values only */
+ if (DCTSIZE != 8)
+ return 0;
+ if (sizeof(JCOEF) != 2)
+ return 0;
+ if (BITS_IN_JSAMPLE != 8)
+ return 0;
+ if (sizeof(JDIMENSION) != 4)
+ return 0;
+ if (sizeof(ISLOW_MULT_TYPE) != 2)
+ return 0;
+
+ if (simd_support & JSIMD_ARM_NEON)
+ return 1;
+
+ return 0;
+}
+
+GLOBAL(void)
+jsimd_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+ JCOEFPTR coef_block, JSAMPARRAY output_buf,
+ JDIMENSION output_col)
+{
+ if (simd_support & JSIMD_ARM_NEON)
+ jsimd_idct_2x2_neon(compptr->dct_table, coef_block, output_buf,
+ output_col);
+}
+
+GLOBAL(void)
+jsimd_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+ JCOEFPTR coef_block, JSAMPARRAY output_buf,
+ JDIMENSION output_col)
+{
+ if (simd_support & JSIMD_ARM_NEON)
+ jsimd_idct_4x4_neon(compptr->dct_table, coef_block, output_buf,
+ output_col);
+}
+
+GLOBAL(int)
+jsimd_can_idct_islow (void)
+{
+ init_simd();
+
+ /* The code is optimised for these values only */
+ if (DCTSIZE != 8)
+ return 0;
+ if (sizeof(JCOEF) != 2)
+ return 0;
+ if (BITS_IN_JSAMPLE != 8)
+ return 0;
+ if (sizeof(JDIMENSION) != 4)
+ return 0;
+ if (sizeof(ISLOW_MULT_TYPE) != 2)
+ return 0;
+
+ if (simd_support & JSIMD_ARM_NEON)
+ return 1;
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_idct_ifast (void)
+{
+ init_simd();
+
+ /* The code is optimised for these values only */
+ if (DCTSIZE != 8)
+ return 0;
+ if (sizeof(JCOEF) != 2)
+ return 0;
+ if (BITS_IN_JSAMPLE != 8)
+ return 0;
+ if (sizeof(JDIMENSION) != 4)
+ return 0;
+ if (sizeof(IFAST_MULT_TYPE) != 2)
+ return 0;
+ if (IFAST_SCALE_BITS != 2)
+ return 0;
+
+ if (simd_support & JSIMD_ARM_NEON)
+ return 1;
+
+ return 0;
+}
+
+GLOBAL(int)
+jsimd_can_idct_float (void)
+{
+ init_simd();
+
+ return 0;
+}
+
+GLOBAL(void)
+jsimd_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+ JCOEFPTR coef_block, JSAMPARRAY output_buf,
+ JDIMENSION output_col)
+{
+ if (simd_support & JSIMD_ARM_NEON)
+ jsimd_idct_islow_neon(compptr->dct_table, coef_block, output_buf,
+ output_col);
+}
+
+GLOBAL(void)
+jsimd_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+ JCOEFPTR coef_block, JSAMPARRAY output_buf,
+ JDIMENSION output_col)
+{
+ if (simd_support & JSIMD_ARM_NEON)
+ jsimd_idct_ifast_neon(compptr->dct_table, coef_block, output_buf,
+ output_col);
+}
+
+GLOBAL(void)
+jsimd_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr,
+ JCOEFPTR coef_block, JSAMPARRAY output_buf,
+ JDIMENSION output_col)
+{
+}
--- /dev/null
+/*
+ * ARMv8 NEON optimizations for libjpeg-turbo
+ *
+ * Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Author: Siarhei Siamashka <siarhei.siamashka@nokia.com>
+ * Copyright (C) 2013-2014, Linaro Limited
+ * Author: Ragesh Radhakrishnan <ragesh.r@linaro.org>
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits /* mark stack as non-executable */
+#endif
+
+.text
+.arch armv8-a+fp+simd
+
+
+#define RESPECT_STRICT_ALIGNMENT 1
+
+
+/*****************************************************************************/
+
+/* Supplementary macro for setting function attributes */
+.macro asm_function fname
+#ifdef __APPLE__
+ .globl _\fname
+_\fname:
+#else
+ .global \fname
+#ifdef __ELF__
+ .hidden \fname
+ .type \fname, %function
+#endif
+\fname:
+#endif
+.endm
+
+/* Transpose elements of single 128 bit registers */
+.macro transpose_single x0,x1,xi,xilen,literal
+ ins \xi\xilen[0], \x0\xilen[0]
+ ins \x1\xilen[0], \x0\xilen[1]
+ trn1 \x0\literal, \x0\literal, \x1\literal
+ trn2 \x1\literal, \xi\literal, \x1\literal
+.endm
+
+/* Transpose elements of 2 differnet registers */
+.macro transpose x0,x1,xi,xilen,literal
+ mov \xi\xilen, \x0\xilen
+ trn1 \x0\literal, \x0\literal, \x1\literal
+ trn2 \x1\literal, \xi\literal, \x1\literal
+.endm
+
+/* Transpose a block of 4x4 coefficients in four 64-bit registers */
+.macro transpose_4x4_32 x0,x0len x1,x1len x2,x2len x3,x3len,xi,xilen
+ mov \xi\xilen, \x0\xilen
+ trn1 \x0\x0len, \x0\x0len, \x2\x2len
+ trn2 \x2\x2len, \xi\x0len, \x2\x2len
+ mov \xi\xilen, \x1\xilen
+ trn1 \x1\x1len, \x1\x1len, \x3\x3len
+ trn2 \x3\x3len, \xi\x1len, \x3\x3len
+.endm
+
+.macro transpose_4x4_16 x0,x0len x1,x1len, x2,x2len, x3,x3len,xi,xilen
+ mov \xi\xilen, \x0\xilen
+ trn1 \x0\x0len, \x0\x0len, \x1\x1len
+ trn2 \x1\x2len, \xi\x0len, \x1\x2len
+ mov \xi\xilen, \x2\xilen
+ trn1 \x2\x2len, \x2\x2len, \x3\x3len
+ trn2 \x3\x2len, \xi\x1len, \x3\x3len
+.endm
+
+.macro transpose_4x4 x0, x1, x2, x3,x5
+ transpose_4x4_16 \x0,.4h, \x1,.4h, \x2,.4h,\x3,.4h,\x5,.16b
+ transpose_4x4_32 \x0,.2s, \x1,.2s, \x2,.2s,\x3,.2s,\x5,.16b
+.endm
+
+
+#define CENTERJSAMPLE 128
+
+/*****************************************************************************/
+
+/*
+ * Perform dequantization and inverse DCT on one block of coefficients.
+ *
+ * GLOBAL(void)
+ * jsimd_idct_islow_neon (void * dct_table, JCOEFPTR coef_block,
+ * JSAMPARRAY output_buf, JDIMENSION output_col)
+ */
+
+#define FIX_0_298631336 (2446)
+#define FIX_0_390180644 (3196)
+#define FIX_0_541196100 (4433)
+#define FIX_0_765366865 (6270)
+#define FIX_0_899976223 (7373)
+#define FIX_1_175875602 (9633)
+#define FIX_1_501321110 (12299)
+#define FIX_1_847759065 (15137)
+#define FIX_1_961570560 (16069)
+#define FIX_2_053119869 (16819)
+#define FIX_2_562915447 (20995)
+#define FIX_3_072711026 (25172)
+
+#define FIX_1_175875602_MINUS_1_961570560 (FIX_1_175875602 - FIX_1_961570560)
+#define FIX_1_175875602_MINUS_0_390180644 (FIX_1_175875602 - FIX_0_390180644)
+#define FIX_0_541196100_MINUS_1_847759065 (FIX_0_541196100 - FIX_1_847759065)
+#define FIX_3_072711026_MINUS_2_562915447 (FIX_3_072711026 - FIX_2_562915447)
+#define FIX_0_298631336_MINUS_0_899976223 (FIX_0_298631336 - FIX_0_899976223)
+#define FIX_1_501321110_MINUS_0_899976223 (FIX_1_501321110 - FIX_0_899976223)
+#define FIX_2_053119869_MINUS_2_562915447 (FIX_2_053119869 - FIX_2_562915447)
+#define FIX_0_541196100_PLUS_0_765366865 (FIX_0_541196100 + FIX_0_765366865)
+
+/*
+ * Reference SIMD-friendly 1-D ISLOW iDCT C implementation.
+ * Uses some ideas from the comments in 'simd/jiss2int-64.asm'
+ */
+#define REF_1D_IDCT(xrow0, xrow1, xrow2, xrow3, xrow4, xrow5, xrow6, xrow7) \
+{ \
+ DCTELEM row0, row1, row2, row3, row4, row5, row6, row7; \
+ INT32 q1, q2, q3, q4, q5, q6, q7; \
+ INT32 tmp11_plus_tmp2, tmp11_minus_tmp2; \
+ \
+ /* 1-D iDCT input data */ \
+ row0 = xrow0; \
+ row1 = xrow1; \
+ row2 = xrow2; \
+ row3 = xrow3; \
+ row4 = xrow4; \
+ row5 = xrow5; \
+ row6 = xrow6; \
+ row7 = xrow7; \
+ \
+ q5 = row7 + row3; \
+ q4 = row5 + row1; \
+ q6 = MULTIPLY(q5, FIX_1_175875602_MINUS_1_961570560) + \
+ MULTIPLY(q4, FIX_1_175875602); \
+ q7 = MULTIPLY(q5, FIX_1_175875602) + \
+ MULTIPLY(q4, FIX_1_175875602_MINUS_0_390180644); \
+ q2 = MULTIPLY(row2, FIX_0_541196100) + \
+ MULTIPLY(row6, FIX_0_541196100_MINUS_1_847759065); \
+ q4 = q6; \
+ q3 = ((INT32) row0 - (INT32) row4) << 13; \
+ q6 += MULTIPLY(row5, -FIX_2_562915447) + \
+ MULTIPLY(row3, FIX_3_072711026_MINUS_2_562915447); \
+ /* now we can use q1 (reloadable constants have been used up) */ \
+ q1 = q3 + q2; \
+ q4 += MULTIPLY(row7, FIX_0_298631336_MINUS_0_899976223) + \
+ MULTIPLY(row1, -FIX_0_899976223); \
+ q5 = q7; \
+ q1 = q1 + q6; \
+ q7 += MULTIPLY(row7, -FIX_0_899976223) + \
+ MULTIPLY(row1, FIX_1_501321110_MINUS_0_899976223); \
+ \
+ /* (tmp11 + tmp2) has been calculated (out_row1 before descale) */ \
+ tmp11_plus_tmp2 = q1; \
+ row1 = 0; \
+ \
+ q1 = q1 - q6; \
+ q5 += MULTIPLY(row5, FIX_2_053119869_MINUS_2_562915447) + \
+ MULTIPLY(row3, -FIX_2_562915447); \
+ q1 = q1 - q6; \
+ q6 = MULTIPLY(row2, FIX_0_541196100_PLUS_0_765366865) + \
+ MULTIPLY(row6, FIX_0_541196100); \
+ q3 = q3 - q2; \
+ \
+ /* (tmp11 - tmp2) has been calculated (out_row6 before descale) */ \
+ tmp11_minus_tmp2 = q1; \
+ \
+ q1 = ((INT32) row0 + (INT32) row4) << 13; \
+ q2 = q1 + q6; \
+ q1 = q1 - q6; \
+ \
+ /* pick up the results */ \
+ tmp0 = q4; \
+ tmp1 = q5; \
+ tmp2 = (tmp11_plus_tmp2 - tmp11_minus_tmp2) / 2; \
+ tmp3 = q7; \
+ tmp10 = q2; \
+ tmp11 = (tmp11_plus_tmp2 + tmp11_minus_tmp2) / 2; \
+ tmp12 = q3; \
+ tmp13 = q1; \
+}
+
+#define XFIX_0_899976223 v0.4h[0]
+#define XFIX_0_541196100 v0.4h[1]
+#define XFIX_2_562915447 v0.4h[2]
+#define XFIX_0_298631336_MINUS_0_899976223 v0.4h[3]
+#define XFIX_1_501321110_MINUS_0_899976223 v1.4h[0]
+#define XFIX_2_053119869_MINUS_2_562915447 v1.4h[1]
+#define XFIX_0_541196100_PLUS_0_765366865 v1.4h[2]
+#define XFIX_1_175875602 v1.4h[3]
+#define XFIX_1_175875602_MINUS_0_390180644 v2.4h[0]
+#define XFIX_0_541196100_MINUS_1_847759065 v2.4h[1]
+#define XFIX_3_072711026_MINUS_2_562915447 v2.4h[2]
+#define XFIX_1_175875602_MINUS_1_961570560 v2.4h[3]
+
+.balign 16
+jsimd_idct_islow_neon_consts:
+ .short FIX_0_899976223 /* d0[0] */
+ .short FIX_0_541196100 /* d0[1] */
+ .short FIX_2_562915447 /* d0[2] */
+ .short FIX_0_298631336_MINUS_0_899976223 /* d0[3] */
+ .short FIX_1_501321110_MINUS_0_899976223 /* d1[0] */
+ .short FIX_2_053119869_MINUS_2_562915447 /* d1[1] */
+ .short FIX_0_541196100_PLUS_0_765366865 /* d1[2] */
+ .short FIX_1_175875602 /* d1[3] */
+ /* reloadable constants */
+ .short FIX_1_175875602_MINUS_0_390180644 /* d2[0] */
+ .short FIX_0_541196100_MINUS_1_847759065 /* d2[1] */
+ .short FIX_3_072711026_MINUS_2_562915447 /* d2[2] */
+ .short FIX_1_175875602_MINUS_1_961570560 /* d2[3] */
+
+asm_function jsimd_idct_islow_neon
+
+ DCT_TABLE .req x0
+ COEF_BLOCK .req x1
+ OUTPUT_BUF .req x2
+ OUTPUT_COL .req x3
+ TMP1 .req x0
+ TMP2 .req x1
+ TMP3 .req x2
+ TMP4 .req x15
+
+ ROW0L .req v16
+ ROW0R .req v17
+ ROW1L .req v18
+ ROW1R .req v19
+ ROW2L .req v20
+ ROW2R .req v21
+ ROW3L .req v22
+ ROW3R .req v23
+ ROW4L .req v24
+ ROW4R .req v25
+ ROW5L .req v26
+ ROW5R .req v27
+ ROW6L .req v28
+ ROW6R .req v29
+ ROW7L .req v30
+ ROW7R .req v31
+ /* Save all NEON registers and x15 (32 NEON registers * 8 bytes + 16) */
+ sub sp, sp, 272
+ str x15, [sp], 16
+ adr x15, jsimd_idct_islow_neon_consts
+ st1 {v0.8b - v3.8b}, [sp], 32
+ st1 {v4.8b - v7.8b}, [sp], 32
+ st1 {v8.8b - v11.8b}, [sp], 32
+ st1 {v12.8b - v15.8b}, [sp], 32
+ st1 {v16.8b - v19.8b}, [sp], 32
+ st1 {v20.8b - v23.8b}, [sp], 32
+ st1 {v24.8b - v27.8b}, [sp], 32
+ st1 {v28.8b - v31.8b}, [sp], 32
+ ld1 {v16.4h, v17.4h, v18.4h, v19.4h}, [COEF_BLOCK], 32
+ ld1 {v0.4h, v1.4h, v2.4h, v3.4h}, [DCT_TABLE], 32
+ ld1 {v20.4h, v21.4h, v22.4h, v23.4h}, [COEF_BLOCK], 32
+ mul v16.4h, v16.4h, v0.4h
+ mul v17.4h, v17.4h, v1.4h
+ ins v16.2d[1], v17.2d[0] /* 128 bit q8 */
+ ld1 {v4.4h, v5.4h, v6.4h, v7.4h}, [DCT_TABLE], 32
+ mul v18.4h, v18.4h, v2.4h
+ mul v19.4h, v19.4h, v3.4h
+ ins v18.2d[1], v19.2d[0] /* 128 bit q9 */
+ ld1 {v24.4h, v25.4h, v26.4h, v27.4h}, [COEF_BLOCK], 32
+ mul v20.4h, v20.4h, v4.4h
+ mul v21.4h, v21.4h, v5.4h
+ ins v20.2d[1], v21.2d[0] /* 128 bit q10 */
+ ld1 {v0.4h, v1.4h, v2.4h, v3.4h}, [DCT_TABLE], 32
+ mul v22.4h, v22.4h, v6.4h
+ mul v23.4h, v23.4h, v7.4h
+ ins v22.2d[1], v23.2d[0] /* 128 bit q11 */
+ ld1 {v28.4h, v29.4h, v30.4h, v31.4h}, [COEF_BLOCK]
+ mul v24.4h, v24.4h, v0.4h
+ mul v25.4h, v25.4h, v1.4h
+ ins v24.2d[1], v25.2d[0] /* 128 bit q12 */
+ ld1 {v4.4h, v5.4h, v6.4h, v7.4h}, [DCT_TABLE], 32
+ mul v28.4h, v28.4h, v4.4h
+ mul v29.4h, v29.4h, v5.4h
+ ins v28.2d[1], v29.2d[0] /* 128 bit q14 */
+ mul v26.4h, v26.4h, v2.4h
+ mul v27.4h, v27.4h, v3.4h
+ ins v26.2d[1], v27.2d[0] /* 128 bit q13 */
+ ld1 {v0.4h, v1.4h, v2.4h, v3.4h}, [x15] /* load constants */
+ add x15, x15, #16
+ mul v30.4h, v30.4h, v6.4h
+ mul v31.4h, v31.4h, v7.4h
+ ins v30.2d[1], v31.2d[0] /* 128 bit q15 */
+ /* Go to the bottom of the stack */
+ sub sp, sp, 352
+ stp x4, x5, [sp], 16
+ st1 {v8.4h - v11.4h}, [sp], 32 /* save NEON registers */
+ st1 {v12.4h - v15.4h}, [sp], 32
+ /* 1-D IDCT, pass 1, left 4x8 half */
+ add v4.4h, ROW7L.4h, ROW3L.4h
+ add v5.4h, ROW5L.4h, ROW1L.4h
+ smull v12.4s, v4.4h, XFIX_1_175875602_MINUS_1_961570560
+ smlal v12.4s, v5.4h, XFIX_1_175875602
+ smull v14.4s, v4.4h, XFIX_1_175875602
+ /* Check for the zero coefficients in the right 4x8 half */
+ smlal v14.4s, v5.4h, XFIX_1_175875602_MINUS_0_390180644
+ ssubl v6.4s, ROW0L.4h, ROW4L.4h
+ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 1 * 8))]
+ smull v4.4s, ROW2L.4h, XFIX_0_541196100
+ smlal v4.4s, ROW6L.4h, XFIX_0_541196100_MINUS_1_847759065
+ orr x0, x4, x5
+ mov v8.16b, v12.16b
+ smlsl v12.4s, ROW5L.4h, XFIX_2_562915447
+ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 2 * 8))]
+ smlal v12.4s, ROW3L.4h, XFIX_3_072711026_MINUS_2_562915447
+ shl v6.4s, v6.4s, #13
+ orr x0, x0, x4
+ smlsl v8.4s, ROW1L.4h, XFIX_0_899976223
+ orr x0, x0 , x5
+ add v2.4s, v6.4s, v4.4s
+ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 3 * 8))]
+ mov v10.16b, v14.16b
+ add v2.4s, v2.4s, v12.4s
+ orr x0, x0, x4
+ smlsl v14.4s, ROW7L.4h, XFIX_0_899976223
+ orr x0, x0, x5
+ smlal v14.4s, ROW1L.4h, XFIX_1_501321110_MINUS_0_899976223
+ rshrn ROW1L.4h, v2.4s, #11
+ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 4 * 8))]
+ sub v2.4s, v2.4s, v12.4s
+ smlal v10.4s, ROW5L.4h, XFIX_2_053119869_MINUS_2_562915447
+ orr x0, x0, x4
+ smlsl v10.4s, ROW3L.4h, XFIX_2_562915447
+ orr x0, x0, x5
+ sub v2.4s, v2.4s, v12.4s
+ smull v12.4s, ROW2L.4h, XFIX_0_541196100_PLUS_0_765366865
+ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 5 * 8))]
+ smlal v12.4s, ROW6L.4h, XFIX_0_541196100
+ sub v6.4s, v6.4s, v4.4s
+ orr x0, x0, x4
+ rshrn ROW6L.4h, v2.4s, #11
+ orr x0, x0, x5
+ add v2.4s, v6.4s, v10.4s
+ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 6 * 8))]
+ sub v6.4s, v6.4s, v10.4s
+ saddl v10.4s, ROW0L.4h, ROW4L.4h
+ orr x0, x0, x4
+ rshrn ROW2L.4h, v2.4s, #11
+ orr x0, x0, x5
+ rshrn ROW5L.4h, v6.4s, #11
+ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 7 * 8))]
+ shl v10.4s, v10.4s, #13
+ smlal v8.4s, ROW7L.4h, XFIX_0_298631336_MINUS_0_899976223
+ orr x0, x0, x4
+ add v4.4s, v10.4s, v12.4s
+ orr x0, x0, x5
+ cmp x0, #0 /* orrs instruction removed */
+ sub v2.4s, v10.4s, v12.4s
+ add v12.4s, v4.4s, v14.4s
+ ldp w4, w5, [COEF_BLOCK, #(-96 + 2 * (4 + 0 * 8))]
+ sub v4.4s, v4.4s, v14.4s
+ add v10.4s, v2.4s, v8.4s
+ orr x0, x4, x5
+ sub v6.4s, v2.4s, v8.4s
+ /* pop {x4, x5} */
+ sub sp, sp, 80
+ ldp x4, x5, [sp], 16
+ rshrn ROW7L.4h, v4.4s, #11
+ rshrn ROW3L.4h, v10.4s, #11
+ rshrn ROW0L.4h, v12.4s, #11
+ rshrn ROW4L.4h, v6.4s, #11
+
+ beq 3f /* Go to do some special handling for the sparse right 4x8 half */
+
+ /* 1-D IDCT, pass 1, right 4x8 half */
+ ld1 {v2.4h}, [x15] /* reload constants */
+ add v10.4h, ROW7R.4h, ROW3R.4h
+ add v8.4h, ROW5R.4h, ROW1R.4h
+ /* Transpose ROW6L <-> ROW7L (v3 available free register) */
+ transpose ROW6L, ROW7L, v3, .16b, .4h
+ smull v12.4s, v10.4h, XFIX_1_175875602_MINUS_1_961570560
+ smlal v12.4s, v8.4h, XFIX_1_175875602
+ /* Transpose ROW2L <-> ROW3L (v3 available free register) */
+ transpose ROW2L, ROW3L, v3, .16b, .4h
+ smull v14.4s, v10.4h, XFIX_1_175875602
+ smlal v14.4s, v8.4h, XFIX_1_175875602_MINUS_0_390180644
+ /* Transpose ROW0L <-> ROW1L (v3 available free register) */
+ transpose ROW0L, ROW1L, v3, .16b, .4h
+ ssubl v6.4s, ROW0R.4h, ROW4R.4h
+ smull v4.4s, ROW2R.4h, XFIX_0_541196100
+ smlal v4.4s, ROW6R.4h, XFIX_0_541196100_MINUS_1_847759065
+ /* Transpose ROW4L <-> ROW5L (v3 available free register) */
+ transpose ROW4L, ROW5L, v3, .16b, .4h
+ mov v8.16b, v12.16b
+ smlsl v12.4s, ROW5R.4h, XFIX_2_562915447
+ smlal v12.4s, ROW3R.4h, XFIX_3_072711026_MINUS_2_562915447
+ /* Transpose ROW1L <-> ROW3L (v3 available free register) */
+ transpose ROW1L, ROW3L, v3, .16b, .2s
+ shl v6.4s, v6.4s, #13
+ smlsl v8.4s, ROW1R.4h, XFIX_0_899976223
+ /* Transpose ROW4L <-> ROW6L (v3 available free register) */
+ transpose ROW4L, ROW6L, v3, .16b, .2s
+ add v2.4s, v6.4s, v4.4s
+ mov v10.16b, v14.16b
+ add v2.4s, v2.4s, v12.4s
+ /* Transpose ROW0L <-> ROW2L (v3 available free register) */
+ transpose ROW0L, ROW2L, v3, .16b, .2s
+ smlsl v14.4s, ROW7R.4h, XFIX_0_899976223
+ smlal v14.4s, ROW1R.4h, XFIX_1_501321110_MINUS_0_899976223
+ rshrn ROW1R.4h, v2.4s, #11
+ /* Transpose ROW5L <-> ROW7L (v3 available free register) */
+ transpose ROW5L, ROW7L, v3, .16b, .2s
+ sub v2.4s, v2.4s, v12.4s
+ smlal v10.4s, ROW5R.4h, XFIX_2_053119869_MINUS_2_562915447
+ smlsl v10.4s, ROW3R.4h, XFIX_2_562915447
+ sub v2.4s, v2.4s, v12.4s
+ smull v12.4s, ROW2R.4h, XFIX_0_541196100_PLUS_0_765366865
+ smlal v12.4s, ROW6R.4h, XFIX_0_541196100
+ sub v6.4s, v6.4s, v4.4s
+ rshrn ROW6R.4h, v2.4s, #11
+ add v2.4s, v6.4s, v10.4s
+ sub v6.4s, v6.4s, v10.4s
+ saddl v10.4s, ROW0R.4h, ROW4R.4h
+ rshrn ROW2R.4h, v2.4s, #11
+ rshrn ROW5R.4h, v6.4s, #11
+ shl v10.4s, v10.4s, #13
+ smlal v8.4s, ROW7R.4h, XFIX_0_298631336_MINUS_0_899976223
+ add v4.4s, v10.4s, v12.4s
+ sub v2.4s, v10.4s, v12.4s
+ add v12.4s, v4.4s, v14.4s
+ sub v4.4s, v4.4s, v14.4s
+ add v10.4s, v2.4s, v8.4s
+ sub v6.4s, v2.4s, v8.4s
+ rshrn ROW7R.4h, v4.4s, #11
+ rshrn ROW3R.4h, v10.4s, #11
+ rshrn ROW0R.4h, v12.4s, #11
+ rshrn ROW4R.4h, v6.4s, #11
+ /* Transpose right 4x8 half */
+ transpose ROW6R, ROW7R, v3, .16b, .4h
+ transpose ROW2R, ROW3R, v3, .16b, .4h
+ transpose ROW0R, ROW1R, v3, .16b, .4h
+ transpose ROW4R, ROW5R, v3, .16b, .4h
+ transpose ROW1R, ROW3R, v3, .16b, .2s
+ transpose ROW4R, ROW6R, v3, .16b, .2s
+ transpose ROW0R, ROW2R, v3, .16b, .2s
+ transpose ROW5R, ROW7R, v3, .16b, .2s
+
+1: /* 1-D IDCT, pass 2 (normal variant), left 4x8 half */
+ ld1 {v2.4h}, [x15] /* reload constants */
+ smull v12.4S, ROW1R.4h, XFIX_1_175875602 /* ROW5L.4h <-> ROW1R.4h */
+ smlal v12.4s, ROW1L.4h, XFIX_1_175875602
+ smlal v12.4s, ROW3R.4h, XFIX_1_175875602_MINUS_1_961570560 /* ROW7L.4h <-> ROW3R.4h */
+ smlal v12.4s, ROW3L.4h, XFIX_1_175875602_MINUS_1_961570560
+ smull v14.4s, ROW3R.4h, XFIX_1_175875602 /* ROW7L.4h <-> ROW3R.4h */
+ smlal v14.4s, ROW3L.4h, XFIX_1_175875602
+ smlal v14.4s, ROW1R.4h, XFIX_1_175875602_MINUS_0_390180644 /* ROW5L.4h <-> ROW1R.4h */
+ smlal v14.4s, ROW1L.4h, XFIX_1_175875602_MINUS_0_390180644
+ ssubl v6.4s, ROW0L.4h, ROW0R.4h /* ROW4L.4h <-> ROW0R.4h */
+ smull v4.4s, ROW2L.4h, XFIX_0_541196100
+ smlal v4.4s, ROW2R.4h, XFIX_0_541196100_MINUS_1_847759065 /* ROW6L.4h <-> ROW2R.4h */
+ mov v8.16b, v12.16b
+ smlsl v12.4s, ROW1R.4h, XFIX_2_562915447 /* ROW5L.4h <-> ROW1R.4h */
+ smlal v12.4s, ROW3L.4h, XFIX_3_072711026_MINUS_2_562915447
+ shl v6.4s, v6.4s, #13
+ smlsl v8.4s, ROW1L.4h, XFIX_0_899976223
+ add v2.4s, v6.4s, v4.4s
+ mov v10.16b, v14.16b
+ add v2.4s, v2.4s, v12.4s
+ smlsl v14.4s, ROW3R.4h, XFIX_0_899976223 /* ROW7L.4h <-> ROW3R.4h */
+ smlal v14.4s, ROW1L.4h, XFIX_1_501321110_MINUS_0_899976223
+ shrn ROW1L.4h, v2.4s, #16
+ sub v2.4s, v2.4s, v12.4s
+ smlal v10.4s, ROW1R.4h, XFIX_2_053119869_MINUS_2_562915447 /* ROW5L.4h <-> ROW1R.4h */
+ smlsl v10.4s, ROW3L.4h, XFIX_2_562915447
+ sub v2.4s, v2.4s, v12.4s
+ smull v12.4s, ROW2L.4h, XFIX_0_541196100_PLUS_0_765366865
+ smlal v12.4s, ROW2R.4h, XFIX_0_541196100 /* ROW6L.4h <-> ROW2R.4h */
+ sub v6.4s, v6.4s, v4.4s
+ shrn ROW2R.4h, v2.4s, #16 /* ROW6L.4h <-> ROW2R.4h */
+ add v2.4s, v6.4s, v10.4s
+ sub v6.4s, v6.4s, v10.4s
+ saddl v10.4s, ROW0L.4h, ROW0R.4h /* ROW4L.4h <-> ROW0R.4h */
+ shrn ROW2L.4h, v2.4s, #16
+ shrn ROW1R.4h, v6.4s, #16 /* ROW5L.4h <-> ROW1R.4h */
+ shl v10.4s, v10.4s, #13
+ smlal v8.4s, ROW3R.4h, XFIX_0_298631336_MINUS_0_899976223 /* ROW7L.4h <-> ROW3R.4h */
+ add v4.4s, v10.4s, v12.4s
+ sub v2.4s, v10.4s, v12.4s
+ add v12.4s, v4.4s, v14.4s
+ sub v4.4s, v4.4s, v14.4s
+ add v10.4s, v2.4s, v8.4s
+ sub v6.4s, v2.4s, v8.4s
+ shrn ROW3R.4h, v4.4s, #16 /* ROW7L.4h <-> ROW3R.4h */
+ shrn ROW3L.4h, v10.4s, #16
+ shrn ROW0L.4h, v12.4s, #16
+ shrn ROW0R.4h, v6.4s, #16 /* ROW4L.4h <-> ROW0R.4h */
+ /* 1-D IDCT, pass 2, right 4x8 half */
+ ld1 {v2.4h}, [x15] /* reload constants */
+ smull v12.4s, ROW5R.4h, XFIX_1_175875602
+ smlal v12.4s, ROW5L.4h, XFIX_1_175875602 /* ROW5L.4h <-> ROW1R.4h */
+ smlal v12.4s, ROW7R.4h, XFIX_1_175875602_MINUS_1_961570560
+ smlal v12.4s, ROW7L.4h, XFIX_1_175875602_MINUS_1_961570560 /* ROW7L.4h <-> ROW3R.4h */
+ smull v14.4s, ROW7R.4h, XFIX_1_175875602
+ smlal v14.4s, ROW7L.4h, XFIX_1_175875602 /* ROW7L.4h <-> ROW3R.4h */
+ smlal v14.4s, ROW5R.4h, XFIX_1_175875602_MINUS_0_390180644
+ smlal v14.4s, ROW5L.4h, XFIX_1_175875602_MINUS_0_390180644 /* ROW5L.4h <-> ROW1R.4h */
+ ssubl v6.4s, ROW4L.4h, ROW4R.4h /* ROW4L.4h <-> ROW0R.4h */
+ smull v4.4s, ROW6L.4h, XFIX_0_541196100 /* ROW6L.4h <-> ROW2R.4h */
+ smlal v4.4s, ROW6R.4h, XFIX_0_541196100_MINUS_1_847759065
+ mov v8.16b, v12.16b
+ smlsl v12.4s, ROW5R.4h, XFIX_2_562915447
+ smlal v12.4s, ROW7L.4h, XFIX_3_072711026_MINUS_2_562915447 /* ROW7L.4h <-> ROW3R.4h */
+ shl v6.4s, v6.4s, #13
+ smlsl v8.4s, ROW5L.4h, XFIX_0_899976223 /* ROW5L.4h <-> ROW1R.4h */
+ add v2.4s, v6.4s, v4.4s
+ mov v10.16b, v14.16b
+ add v2.4s, v2.4s, v12.4s
+ smlsl v14.4s, ROW7R.4h, XFIX_0_899976223
+ smlal v14.4s, ROW5L.4h, XFIX_1_501321110_MINUS_0_899976223 /* ROW5L.4h <-> ROW1R.4h */
+ shrn ROW5L.4h, v2.4s, #16 /* ROW5L.4h <-> ROW1R.4h */
+ sub v2.4s, v2.4s, v12.4s
+ smlal v10.4s, ROW5R.4h, XFIX_2_053119869_MINUS_2_562915447
+ smlsl v10.4s, ROW7L.4h, XFIX_2_562915447 /* ROW7L.4h <-> ROW3R.4h */
+ sub v2.4s, v2.4s, v12.4s
+ smull v12.4s, ROW6L.4h, XFIX_0_541196100_PLUS_0_765366865 /* ROW6L.4h <-> ROW2R.4h */
+ smlal v12.4s, ROW6R.4h, XFIX_0_541196100
+ sub v6.4s, v6.4s, v4.4s
+ shrn ROW6R.4h, v2.4s, #16
+ add v2.4s, v6.4s, v10.4s
+ sub v6.4s, v6.4s, v10.4s
+ saddl v10.4s, ROW4L.4h, ROW4R.4h /* ROW4L.4h <-> ROW0R.4h */
+ shrn ROW6L.4h, v2.4s, #16 /* ROW6L.4h <-> ROW2R.4h */
+ shrn ROW5R.4h, v6.4s, #16
+ shl v10.4s, v10.4s, #13
+ smlal v8.4s, ROW7R.4h, XFIX_0_298631336_MINUS_0_899976223
+ add v4.4s, v10.4s, v12.4s
+ sub v2.4s, v10.4s, v12.4s
+ add v12.4s, v4.4s, v14.4s
+ sub v4.4s, v4.4s, v14.4s
+ add v10.4s, v2.4s, v8.4s
+ sub v6.4s, v2.4s, v8.4s
+ shrn ROW7R.4h, v4.4s, #16
+ shrn ROW7L.4h, v10.4s, #16 /* ROW7L.4h <-> ROW3R.4h */
+ shrn ROW4L.4h, v12.4s, #16 /* ROW4L.4h <-> ROW0R.4h */
+ shrn ROW4R.4h, v6.4s, #16
+
+2: /* Descale to 8-bit and range limit */
+ ins v16.2d[1], v17.2d[0]
+ ins v18.2d[1], v19.2d[0]
+ ins v20.2d[1], v21.2d[0]
+ ins v22.2d[1], v23.2d[0]
+ sqrshrn v16.8b, v16.8h, #2
+ sqrshrn2 v16.16b, v18.8h, #2
+ sqrshrn v18.8b, v20.8h, #2
+ sqrshrn2 v18.16b, v22.8h, #2
+
+ /* vpop {v8.4h - d15.4h} */ /* restore NEON registers */
+ ld1 {v8.4h - v11.4h}, [sp], 32
+ ld1 {v12.4h - v15.4h}, [sp], 32
+ ins v24.2d[1], v25.2d[0]
+
+ sqrshrn v20.8b, v24.8h, #2
+ /* Transpose the final 8-bit samples and do signed->unsigned conversion */
+ /* trn1 v16.8h, v16.8h, v18.8h */
+ transpose v16, v18, v3, .16b, .8h
+ ins v26.2d[1], v27.2d[0]
+ ins v28.2d[1], v29.2d[0]
+ ins v30.2d[1], v31.2d[0]
+ sqrshrn2 v20.16b, v26.8h, #2
+ sqrshrn v22.8b, v28.8h, #2
+ movi v0.16b, #(CENTERJSAMPLE)
+ sqrshrn2 v22.16b, v30.8h, #2
+ transpose_single v16, v17, v3, .2d, .8b
+ transpose_single v18, v19, v3, .2d, .8b
+ add v16.8b, v16.8b, v0.8b
+ add v17.8b, v17.8b, v0.8b
+ add v18.8b, v18.8b, v0.8b
+ add v19.8b, v19.8b, v0.8b
+ transpose v20, v22, v3, .16b, .8h
+ /* Store results to the output buffer */
+ ldp TMP1, TMP2, [OUTPUT_BUF], 16
+ add TMP1, TMP1, OUTPUT_COL
+ add TMP2, TMP2, OUTPUT_COL
+ st1 {v16.8b}, [TMP1]
+ transpose_single v20, v21, v3, .2d, .8b
+ st1 {v17.8b}, [TMP2]
+ ldp TMP1, TMP2, [OUTPUT_BUF], 16
+ add TMP1, TMP1, OUTPUT_COL
+ add TMP2, TMP2, OUTPUT_COL
+ st1 {v18.8b}, [TMP1]
+ add v20.8b, v20.8b, v0.8b
+ add v21.8b, v21.8b, v0.8b
+ st1 {v19.8b}, [TMP2]
+ ldp TMP1, TMP2, [OUTPUT_BUF], 16
+ ldp TMP3, TMP4, [OUTPUT_BUF]
+ add TMP1, TMP1, OUTPUT_COL
+ add TMP2, TMP2, OUTPUT_COL
+ add TMP3, TMP3, OUTPUT_COL
+ add TMP4, TMP4, OUTPUT_COL
+ transpose_single v22, v23, v3, .2d, .8b
+ st1 {v20.8b}, [TMP1]
+ add v22.8b, v22.8b, v0.8b
+ add v23.8b, v23.8b, v0.8b
+ st1 {v21.8b}, [TMP2]
+ st1 {v22.8b}, [TMP3]
+ st1 {v23.8b}, [TMP4]
+ ldr x15, [sp], 16
+ ld1 {v0.8b - v3.8b}, [sp], 32
+ ld1 {v4.8b - v7.8b}, [sp], 32
+ ld1 {v8.8b - v11.8b}, [sp], 32
+ ld1 {v12.8b - v15.8b}, [sp], 32
+ ld1 {v16.8b - v19.8b}, [sp], 32
+ ld1 {v20.8b - v23.8b}, [sp], 32
+ ld1 {v24.8b - v27.8b}, [sp], 32
+ ld1 {v28.8b - v31.8b}, [sp], 32
+ blr x30
+
+3: /* Left 4x8 half is done, right 4x8 half contains mostly zeros */
+
+ /* Transpose left 4x8 half */
+ transpose ROW6L, ROW7L, v3, .16b, .4h
+ transpose ROW2L, ROW3L, v3, .16b, .4h
+ transpose ROW0L, ROW1L, v3, .16b, .4h
+ transpose ROW4L, ROW5L, v3, .16b, .4h
+ shl ROW0R.4h, ROW0R.4h, #2 /* PASS1_BITS */
+ transpose ROW1L, ROW3L, v3, .16b, .2s
+ transpose ROW4L, ROW6L, v3, .16b, .2s
+ transpose ROW0L, ROW2L, v3, .16b, .2s
+ transpose ROW5L, ROW7L, v3, .16b, .2s
+ cmp x0, #0
+ beq 4f /* Right 4x8 half has all zeros, go to 'sparse' second pass */
+
+ /* Only row 0 is non-zero for the right 4x8 half */
+ dup ROW1R.4h, ROW0R.4h[1]
+ dup ROW2R.4h, ROW0R.4h[2]
+ dup ROW3R.4h, ROW0R.4h[3]
+ dup ROW4R.4h, ROW0R.4h[0]
+ dup ROW5R.4h, ROW0R.4h[1]
+ dup ROW6R.4h, ROW0R.4h[2]
+ dup ROW7R.4h, ROW0R.4h[3]
+ dup ROW0R.4h, ROW0R.4h[0]
+ b 1b /* Go to 'normal' second pass */
+
+4: /* 1-D IDCT, pass 2 (sparse variant with zero rows 4-7), left 4x8 half */
+ ld1 {v2.4h}, [x15] /* reload constants */
+ smull v12.4s, ROW1L.4h, XFIX_1_175875602
+ smlal v12.4s, ROW3L.4h, XFIX_1_175875602_MINUS_1_961570560
+ smull v14.4s, ROW3L.4h, XFIX_1_175875602
+ smlal v14.4s, ROW1L.4h, XFIX_1_175875602_MINUS_0_390180644
+ smull v4.4s, ROW2L.4h, XFIX_0_541196100
+ sshll v6.4s, ROW0L.4h, #13
+ mov v8.16b, v12.16b
+ smlal v12.4s, ROW3L.4h, XFIX_3_072711026_MINUS_2_562915447
+ smlsl v8.4s, ROW1L.4h, XFIX_0_899976223
+ add v2.4s, v6.4s, v4.4s
+ mov v10.16b, v14.16b
+ smlal v14.4s, ROW1L.4h, XFIX_1_501321110_MINUS_0_899976223
+ add v2.4s, v2.4s, v12.4s
+ add v12.4s, v12.4s, v12.4s
+ smlsl v10.4s, ROW3L.4h, XFIX_2_562915447
+ shrn ROW1L.4h, v2.4s, #16
+ sub v2.4s, v2.4s, v12.4s
+ smull v12.4s, ROW2L.4h, XFIX_0_541196100_PLUS_0_765366865
+ sub v6.4s, v6.4s, v4.4s
+ shrn ROW2R.4h, v2.4s, #16 /* ROW6L.4h <-> ROW2R.4h */
+ add v2.4s, v6.4s, v10.4s
+ sub v6.4s, v6.4s, v10.4s
+ sshll v10.4s, ROW0L.4h, #13
+ shrn ROW2L.4h, v2.4s, #16
+ shrn ROW1R.4h, v6.4s, #16 /* ROW5L.4h <-> ROW1R.4h */
+ add v4.4s, v10.4s, v12.4s
+ sub v2.4s, v10.4s, v12.4s
+ add v12.4s, v4.4s, v14.4s
+ sub v4.4s, v4.4s, v14.4s
+ add v10.4s, v2.4s, v8.4s
+ sub v6.4s, v2.4s, v8.4s
+ shrn ROW3R.4h, v4.4s, #16 /* ROW7L.4h <-> ROW3R.4h */
+ shrn ROW3L.4h, v10.4s, #16
+ shrn ROW0L.4h, v12.4s, #16
+ shrn ROW0R.4h, v6.4s, #16 /* ROW4L.4h <-> ROW0R.4h */
+ /* 1-D IDCT, pass 2 (sparse variant with zero rows 4-7), right 4x8 half */
+ ld1 {v2.4h}, [x15] /* reload constants */
+ smull v12.4s, ROW5L.4h, XFIX_1_175875602
+ smlal v12.4s, ROW7L.4h, XFIX_1_175875602_MINUS_1_961570560
+ smull v14.4s, ROW7L.4h, XFIX_1_175875602
+ smlal v14.4s, ROW5L.4h, XFIX_1_175875602_MINUS_0_390180644
+ smull v4.4s, ROW6L.4h, XFIX_0_541196100
+ sshll v6.4s, ROW4L.4h, #13
+ mov v8.16b, v12.16b
+ smlal v12.4s, ROW7L.4h, XFIX_3_072711026_MINUS_2_562915447
+ smlsl v8.4s, ROW5L.4h, XFIX_0_899976223
+ add v2.4s, v6.4s, v4.4s
+ mov v10.16b, v14.16b
+ smlal v14.4s, ROW5L.4h, XFIX_1_501321110_MINUS_0_899976223
+ add v2.4s, v2.4s, v12.4s
+ add v12.4s, v12.4s, v12.4s
+ smlsl v10.4s, ROW7L.4h, XFIX_2_562915447
+ shrn ROW5L.4h, v2.4s, #16 /* ROW5L.4h <-> ROW1R.4h */
+ sub v2.4s, v2.4s, v12.4s
+ smull v12.4s, ROW6L.4h, XFIX_0_541196100_PLUS_0_765366865
+ sub v6.4s, v6.4s, v4.4s
+ shrn ROW6R.4h, v2.4s, #16
+ add v2.4s, v6.4s, v10.4s
+ sub v6.4s, v6.4s, v10.4s
+ sshll v10.4s, ROW4L.4h, #13
+ shrn ROW6L.4h, v2.4s, #16 /* ROW6L.4h <-> ROW2R.4h */
+ shrn ROW5R.4h, v6.4s, #16
+ add v4.4s, v10.4s, v12.4s
+ sub v2.4s, v10.4s, v12.4s
+ add v12.4s, v4.4s, v14.4s
+ sub v4.4s, v4.4s, v14.4s
+ add v10.4s, v2.4s, v8.4s
+ sub v6.4s, v2.4s, v8.4s
+ shrn ROW7R.4h, v4.4s, #16
+ shrn ROW7L.4h, v10.4s, #16 /* ROW7L.4h <-> ROW3R.4h */
+ shrn ROW4L.4h, v12.4s, #16 /* ROW4L.4h <-> ROW0R.4h */
+ shrn ROW4R.4h, v6.4s, #16
+ b 2b /* Go to epilogue */
+
+ .unreq DCT_TABLE
+ .unreq COEF_BLOCK
+ .unreq OUTPUT_BUF
+ .unreq OUTPUT_COL
+ .unreq TMP1
+ .unreq TMP2
+ .unreq TMP3
+ .unreq TMP4
+
+ .unreq ROW0L
+ .unreq ROW0R
+ .unreq ROW1L
+ .unreq ROW1R
+ .unreq ROW2L
+ .unreq ROW2R
+ .unreq ROW3L
+ .unreq ROW3R
+ .unreq ROW4L
+ .unreq ROW4R
+ .unreq ROW5L
+ .unreq ROW5R
+ .unreq ROW6L
+ .unreq ROW6R
+ .unreq ROW7L
+ .unreq ROW7R
+
+
+/*****************************************************************************/
+
+/*
+ * jsimd_idct_ifast_neon
+ *
+ * This function contains a fast, not so accurate integer implementation of
+ * the inverse DCT (Discrete Cosine Transform). It uses the same calculations
+ * and produces exactly the same output as IJG's original 'jpeg_idct_ifast'
+ * function from jidctfst.c
+ *
+ * Normally 1-D AAN DCT needs 5 multiplications and 29 additions.
+ * But in ARM NEON case some extra additions are required because VQDMULH
+ * instruction can't handle the constants larger than 1. So the expressions
+ * like "x * 1.082392200" have to be converted to "x * 0.082392200 + x",
+ * which introduces an extra addition. Overall, there are 6 extra additions
+ * per 1-D IDCT pass, totalling to 5 VQDMULH and 35 VADD/VSUB instructions.
+ */
+
+#define XFIX_1_082392200 v0.4h[0]
+#define XFIX_1_414213562 v0.4h[1]
+#define XFIX_1_847759065 v0.4h[2]
+#define XFIX_2_613125930 v0.4h[3]
+
+.balign 16
+jsimd_idct_ifast_neon_consts:
+ .short (277 * 128 - 256 * 128) /* XFIX_1_082392200 */
+ .short (362 * 128 - 256 * 128) /* XFIX_1_414213562 */
+ .short (473 * 128 - 256 * 128) /* XFIX_1_847759065 */
+ .short (669 * 128 - 512 * 128) /* XFIX_2_613125930 */
+
+asm_function jsimd_idct_ifast_neon
+
+ DCT_TABLE .req x0
+ COEF_BLOCK .req x1
+ OUTPUT_BUF .req x2
+ OUTPUT_COL .req x3
+ TMP1 .req x0
+ TMP2 .req x1
+ TMP3 .req x2
+ TMP4 .req x22
+ TMP5 .req x23
+
+ /* Load and dequantize coefficients into NEON registers
+ * with the following allocation:
+ * 0 1 2 3 | 4 5 6 7
+ * ---------+--------
+ * 0 | d16 | d17 ( v8.8h )
+ * 1 | d18 | d19 ( v9.8h )
+ * 2 | d20 | d21 ( v10.8h )
+ * 3 | d22 | d23 ( v11.8h )
+ * 4 | d24 | d25 ( v12.8h )
+ * 5 | d26 | d27 ( v13.8h )
+ * 6 | d28 | d29 ( v14.8h )
+ * 7 | d30 | d31 ( v15.8h )
+ */
+ /* Save NEON registers used in fast IDCT */
+ sub sp, sp, #176
+ stp x22, x23, [sp], 16
+ adr x23, jsimd_idct_ifast_neon_consts
+ st1 {v0.8b - v3.8b}, [sp], 32
+ st1 {v4.8b - v7.8b}, [sp], 32
+ st1 {v8.8b - v11.8b}, [sp], 32
+ st1 {v12.8b - v15.8b}, [sp], 32
+ st1 {v16.8b - v19.8b}, [sp], 32
+ ld1 {v8.8h, v9.8h}, [COEF_BLOCK], 32
+ ld1 {v0.8h, v1.8h}, [DCT_TABLE], 32
+ ld1 {v10.8h, v11.8h}, [COEF_BLOCK], 32
+ mul v8.8h, v8.8h, v0.8h
+ ld1 {v2.8h, v3.8h}, [DCT_TABLE], 32
+ mul v9.8h, v9.8h, v1.8h
+ ld1 {v12.8h, v13.8h}, [COEF_BLOCK], 32
+ mul v10.8h, v10.8h, v2.8h
+ ld1 {v0.8h, v1.8h}, [DCT_TABLE], 32
+ mul v11.8h, v11.8h, v3.8h
+ ld1 {v14.8h, v15.8h}, [COEF_BLOCK], 32
+ mul v12.8h, v12.8h, v0.8h
+ ld1 {v2.8h, v3.8h}, [DCT_TABLE], 32
+ mul v14.8h, v14.8h, v2.8h
+ mul v13.8h, v13.8h, v1.8h
+ ld1 {v0.4h}, [x23] /* load constants */
+ mul v15.8h, v15.8h, v3.8h
+
+ /* 1-D IDCT, pass 1 */
+ sub v2.8h, v10.8h, v14.8h
+ add v14.8h, v10.8h, v14.8h
+ sub v1.8h, v11.8h, v13.8h
+ add v13.8h, v11.8h, v13.8h
+ sub v5.8h, v9.8h, v15.8h
+ add v15.8h, v9.8h, v15.8h
+ sqdmulh v4.8h, v2.8h, XFIX_1_414213562
+ sqdmulh v6.8h, v1.8h, XFIX_2_613125930
+ add v3.8h, v1.8h, v1.8h
+ sub v1.8h, v5.8h, v1.8h
+ add v10.8h, v2.8h, v4.8h
+ sqdmulh v4.8h, v1.8h, XFIX_1_847759065
+ sub v2.8h, v15.8h, v13.8h
+ add v3.8h, v3.8h, v6.8h
+ sqdmulh v6.8h, v2.8h, XFIX_1_414213562
+ add v1.8h, v1.8h, v4.8h
+ sqdmulh v4.8h, v5.8h, XFIX_1_082392200
+ sub v10.8h, v10.8h, v14.8h
+ add v2.8h, v2.8h, v6.8h
+ sub v6.8h, v8.8h, v12.8h
+ add v12.8h, v8.8h, v12.8h
+ add v9.8h, v5.8h, v4.8h
+ add v5.8h, v6.8h, v10.8h
+ sub v10.8h, v6.8h, v10.8h
+ add v6.8h, v15.8h, v13.8h
+ add v8.8h, v12.8h, v14.8h
+ sub v3.8h, v6.8h, v3.8h
+ sub v12.8h, v12.8h, v14.8h
+ sub v3.8h, v3.8h, v1.8h
+ sub v1.8h, v9.8h, v1.8h
+ add v2.8h, v3.8h, v2.8h
+ sub v15.8h, v8.8h, v6.8h
+ add v1.8h, v1.8h, v2.8h
+ add v8.8h, v8.8h, v6.8h
+ add v14.8h, v5.8h, v3.8h
+ sub v9.8h, v5.8h, v3.8h
+ sub v13.8h, v10.8h, v2.8h
+ add v10.8h, v10.8h, v2.8h
+ /* Transpose q8-q9 */
+ mov v18.16b, v8.16b
+ trn1 v8.8h, v8.8h, v9.8h
+ trn2 v9.8h, v18.8h, v9.8h
+ sub v11.8h, v12.8h, v1.8h
+ /* Transpose q14-q15 */
+ mov v18.16b, v14.16b
+ trn1 v14.8h, v14.8h, v15.8h
+ trn2 v15.8h, v18.8h, v15.8h
+ add v12.8h, v12.8h, v1.8h
+ /* Transpose q10-q11 */
+ mov v18.16b, v10.16b
+ trn1 v10.8h, v10.8h, v11.8h
+ trn2 v11.8h, v18.8h, v11.8h
+ /* Transpose q12-q13 */
+ mov v18.16b, v12.16b
+ trn1 v12.8h, v12.8h, v13.8h
+ trn2 v13.8h, v18.8h, v13.8h
+ /* Transpose q9-q11 */
+ mov v18.16b, v9.16b
+ trn1 v9.4s, v9.4s, v11.4s
+ trn2 v11.4s, v18.4s, v11.4s
+ /* Transpose q12-q14 */
+ mov v18.16b, v12.16b
+ trn1 v12.4s, v12.4s, v14.4s
+ trn2 v14.4s, v18.4s, v14.4s
+ /* Transpose q8-q10 */
+ mov v18.16b, v8.16b
+ trn1 v8.4s, v8.4s, v10.4s
+ trn2 v10.4s, v18.4s, v10.4s
+ /* Transpose q13-q15 */
+ mov v18.16b, v13.16b
+ trn1 v13.4s, v13.4s, v15.4s
+ trn2 v15.4s, v18.4s, v15.4s
+ /* vswp v14.4h, v10-MSB.4h */
+ umov x22, v14.d[0]
+ ins v14.2d[0], v10.2d[1]
+ ins v10.2d[1], x22
+ /* vswp v13.4h, v9MSB.4h */
+
+ umov x22, v13.d[0]
+ ins v13.2d[0], v9.2d[1]
+ ins v9.2d[1], x22
+ /* 1-D IDCT, pass 2 */
+ sub v2.8h, v10.8h, v14.8h
+ /* vswp v15.4h, v11MSB.4h */
+ umov x22, v15.d[0]
+ ins v15.2d[0], v11.2d[1]
+ ins v11.2d[1], x22
+ add v14.8h, v10.8h, v14.8h
+ /* vswp v12.4h, v8-MSB.4h */
+ umov x22, v12.d[0]
+ ins v12.2d[0], v8.2d[1]
+ ins v8.2d[1], x22
+ sub v1.8h, v11.8h, v13.8h
+ add v13.8h, v11.8h, v13.8h
+ sub v5.8h, v9.8h, v15.8h
+ add v15.8h, v9.8h, v15.8h
+ sqdmulh v4.8h, v2.8h, XFIX_1_414213562
+ sqdmulh v6.8h, v1.8h, XFIX_2_613125930
+ add v3.8h, v1.8h, v1.8h
+ sub v1.8h, v5.8h, v1.8h
+ add v10.8h, v2.8h, v4.8h
+ sqdmulh v4.8h, v1.8h, XFIX_1_847759065
+ sub v2.8h, v15.8h, v13.8h
+ add v3.8h, v3.8h, v6.8h
+ sqdmulh v6.8h, v2.8h, XFIX_1_414213562
+ add v1.8h, v1.8h, v4.8h
+ sqdmulh v4.8h, v5.8h, XFIX_1_082392200
+ sub v10.8h, v10.8h, v14.8h
+ add v2.8h, v2.8h, v6.8h
+ sub v6.8h, v8.8h, v12.8h
+ add v12.8h, v8.8h, v12.8h
+ add v9.8h, v5.8h, v4.8h
+ add v5.8h, v6.8h, v10.8h
+ sub v10.8h, v6.8h, v10.8h
+ add v6.8h, v15.8h, v13.8h
+ add v8.8h, v12.8h, v14.8h
+ sub v3.8h, v6.8h, v3.8h
+ sub v12.8h, v12.8h, v14.8h
+ sub v3.8h, v3.8h, v1.8h
+ sub v1.8h, v9.8h, v1.8h
+ add v2.8h, v3.8h, v2.8h
+ sub v15.8h, v8.8h, v6.8h
+ add v1.8h, v1.8h, v2.8h
+ add v8.8h, v8.8h, v6.8h
+ add v14.8h, v5.8h, v3.8h
+ sub v9.8h, v5.8h, v3.8h
+ sub v13.8h, v10.8h, v2.8h
+ add v10.8h, v10.8h, v2.8h
+ sub v11.8h, v12.8h, v1.8h
+ add v12.8h, v12.8h, v1.8h
+ /* Descale to 8-bit and range limit */
+ movi v0.16b, #0x80
+ sqshrn v8.8b, v8.8h, #5
+ sqshrn2 v8.16b, v9.8h, #5
+ sqshrn v9.8b, v10.8h, #5
+ sqshrn2 v9.16b, v11.8h, #5
+ sqshrn v10.8b, v12.8h, #5
+ sqshrn2 v10.16b, v13.8h, #5
+ sqshrn v11.8b, v14.8h, #5
+ sqshrn2 v11.16b, v15.8h, #5
+ add v8.16b, v8.16b, v0.16b
+ add v9.16b, v9.16b, v0.16b
+ add v10.16b, v10.16b, v0.16b
+ add v11.16b, v11.16b, v0.16b
+ /* Transpose the final 8-bit samples */
+ /* Transpose q8-q9 */
+ mov v18.16b, v8.16b
+ trn1 v8.8h, v8.8h, v9.8h
+ trn2 v9.8h, v18.8h, v9.8h
+ /* Transpose q10-q11 */
+ mov v18.16b, v10.16b
+ trn1 v10.8h, v10.8h, v11.8h
+ trn2 v11.8h, v18.8h, v11.8h
+ /* Transpose q8-q10 */
+ mov v18.16b, v8.16b
+ trn1 v8.4s, v8.4s, v10.4s
+ trn2 v10.4s, v18.4s, v10.4s
+ /* Transpose q9-q11 */
+ mov v18.16b, v9.16b
+ trn1 v9.4s, v9.4s, v11.4s
+ trn2 v11.4s, v18.4s, v11.4s
+ /* make copy */
+ ins v17.2d[0], v8.2d[1]
+ /* Transpose d16-d17-msb */
+ mov v18.16b, v8.16b
+ trn1 v8.8b, v8.8b, v17.8b
+ trn2 v17.8b, v18.8b, v17.8b
+ /* make copy */
+ ins v19.2d[0], v9.2d[1]
+ mov v18.16b, v9.16b
+ trn1 v9.8b, v9.8b, v19.8b
+ trn2 v19.8b, v18.8b, v19.8b
+ /* Store results to the output buffer */
+ ldp TMP1, TMP2, [OUTPUT_BUF], 16
+ add TMP1, TMP1, OUTPUT_COL
+ add TMP2, TMP2, OUTPUT_COL
+ st1 {v8.8b}, [TMP1]
+ st1 {v17.8b}, [TMP2]
+ ldp TMP1, TMP2, [OUTPUT_BUF], 16
+ add TMP1, TMP1, OUTPUT_COL
+ add TMP2, TMP2, OUTPUT_COL
+ st1 {v9.8b}, [TMP1]
+ /* make copy */
+ ins v7.2d[0], v10.2d[1]
+ mov v18.16b, v10.16b
+ trn1 v10.8b, v10.8b, v7.8b
+ trn2 v7.8b, v18.8b, v7.8b
+ st1 {v19.8b}, [TMP2]
+ ldp TMP1, TMP2, [OUTPUT_BUF], 16
+ ldp TMP4, TMP5, [OUTPUT_BUF], 16
+ add TMP1, TMP1, OUTPUT_COL
+ add TMP2, TMP2, OUTPUT_COL
+ add TMP4, TMP4, OUTPUT_COL
+ add TMP5, TMP5, OUTPUT_COL
+ st1 {v10.8b}, [TMP1]
+ /* make copy */
+ ins v16.2d[0], v11.2d[1]
+ mov v18.16b, v11.16b
+ trn1 v11.8b, v11.8b, v16.8b
+ trn2 v16.8b, v18.8b, v16.8b
+ st1 {v7.8b}, [TMP2]
+ st1 {v11.8b}, [TMP4]
+ st1 {v16.8b}, [TMP5]
+ sub sp, sp, #176
+ ldp x22, x23, [sp], 16
+ ld1 {v0.8b - v3.8b}, [sp], 32
+ ld1 {v4.8b - v7.8b}, [sp], 32
+ ld1 {v8.8b - v11.8b}, [sp], 32
+ ld1 {v12.8b - v15.8b}, [sp], 32
+ ld1 {v16.8b - v19.8b}, [sp], 32
+ blr x30
+
+ .unreq DCT_TABLE
+ .unreq COEF_BLOCK
+ .unreq OUTPUT_BUF
+ .unreq OUTPUT_COL
+ .unreq TMP1
+ .unreq TMP2
+ .unreq TMP3
+ .unreq TMP4
+
+
+/*****************************************************************************/
+
+/*
+ * jsimd_idct_4x4_neon
+ *
+ * This function contains inverse-DCT code for getting reduced-size
+ * 4x4 pixels output from an 8x8 DCT block. It uses the same calculations
+ * and produces exactly the same output as IJG's original 'jpeg_idct_4x4'
+ * function from jpeg-6b (jidctred.c).
+ *
+ * NOTE: jpeg-8 has an improved implementation of 4x4 inverse-DCT, which
+ * requires much less arithmetic operations and hence should be faster.
+ * The primary purpose of this particular NEON optimized function is
+ * bit exact compatibility with jpeg-6b.
+ *
+ * TODO: a bit better instructions scheduling can be achieved by expanding
+ * idct_helper/transpose_4x4 macros and reordering instructions,
+ * but readability will suffer somewhat.
+ */
+
+#define CONST_BITS 13
+
+#define FIX_0_211164243 (1730) /* FIX(0.211164243) */
+#define FIX_0_509795579 (4176) /* FIX(0.509795579) */
+#define FIX_0_601344887 (4926) /* FIX(0.601344887) */
+#define FIX_0_720959822 (5906) /* FIX(0.720959822) */
+#define FIX_0_765366865 (6270) /* FIX(0.765366865) */
+#define FIX_0_850430095 (6967) /* FIX(0.850430095) */
+#define FIX_0_899976223 (7373) /* FIX(0.899976223) */
+#define FIX_1_061594337 (8697) /* FIX(1.061594337) */
+#define FIX_1_272758580 (10426) /* FIX(1.272758580) */
+#define FIX_1_451774981 (11893) /* FIX(1.451774981) */
+#define FIX_1_847759065 (15137) /* FIX(1.847759065) */
+#define FIX_2_172734803 (17799) /* FIX(2.172734803) */
+#define FIX_2_562915447 (20995) /* FIX(2.562915447) */
+#define FIX_3_624509785 (29692) /* FIX(3.624509785) */
+
+.balign 16
+jsimd_idct_4x4_neon_consts:
+ .short FIX_1_847759065 /* v0.4h[0] */
+ .short -FIX_0_765366865 /* v0.4h[1] */
+ .short -FIX_0_211164243 /* v0.4h[2] */
+ .short FIX_1_451774981 /* v0.4h[3] */
+ .short -FIX_2_172734803 /* d1[0] */
+ .short FIX_1_061594337 /* d1[1] */
+ .short -FIX_0_509795579 /* d1[2] */
+ .short -FIX_0_601344887 /* d1[3] */
+ .short FIX_0_899976223 /* v2.4h[0] */
+ .short FIX_2_562915447 /* v2.4h[1] */
+ .short 1 << (CONST_BITS+1) /* v2.4h[2] */
+ .short 0 /* v2.4h[3] */
+
+.macro idct_helper x4, x6, x8, x10, x12, x14, x16, shift, y26, y27, y28, y29
+ smull v28.4s, \x4, v2.4h[2]
+ smlal v28.4s, \x8, v0.4h[0]
+ smlal v28.4s, \x14, v0.4h[1]
+
+ smull v26.4s, \x16, v1.4h[2]
+ smlal v26.4s, \x12, v1.4h[3]
+ smlal v26.4s, \x10, v2.4h[0]
+ smlal v26.4s, \x6, v2.4h[1]
+
+ smull v30.4s, \x4, v2.4h[2]
+ smlsl v30.4s, \x8, v0.4h[0]
+ smlsl v30.4s, \x14, v0.4h[1]
+
+ smull v24.4s, \x16, v0.4h[2]
+ smlal v24.4s, \x12, v0.4h[3]
+ smlal v24.4s, \x10, v1.4h[0]
+ smlal v24.4s, \x6, v1.4h[1]
+
+ add v20.4s, v28.4s, v26.4s
+ sub v28.4s, v28.4s, v26.4s
+
+.if \shift > 16
+ srshr v20.4s, v20.4s, #\shift
+ srshr v28.4s, v28.4s, #\shift
+ xtn \y26, v20.4s
+ xtn \y29, v28.4s
+.else
+ rshrn \y26, v20.4s, #\shift
+ rshrn \y29, v28.4s, #\shift
+.endif
+
+ add v20.4s, v30.4s, v24.4s
+ sub v30.4s, v30.4s, v24.4s
+
+.if \shift > 16
+ srshr v20.4s, v20.4s, #\shift
+ srshr v30.4s, v30.4s, #\shift
+ xtn \y27, v20.4s
+ xtn \y28, v30.4s
+.else
+ rshrn \y27, v20.4s, #\shift
+ rshrn \y28, v30.4s, #\shift
+.endif
+
+.endm
+
+asm_function jsimd_idct_4x4_neon
+
+ DCT_TABLE .req x0
+ COEF_BLOCK .req x1
+ OUTPUT_BUF .req x2
+ OUTPUT_COL .req x3
+ TMP1 .req x0
+ TMP2 .req x1
+ TMP3 .req x2
+ TMP4 .req x15
+
+ /* Save all used NEON registers */
+ sub sp, sp, 272
+ str x15, [sp], 16
+ /* Load constants (v3.4h is just used for padding) */
+ adr TMP4, jsimd_idct_4x4_neon_consts
+ st1 {v0.8b - v3.8b}, [sp], 32
+ st1 {v4.8b - v7.8b}, [sp], 32
+ st1 {v8.8b - v11.8b}, [sp], 32
+ st1 {v12.8b - v15.8b}, [sp], 32
+ st1 {v16.8b - v19.8b}, [sp], 32
+ st1 {v20.8b - v23.8b}, [sp], 32
+ st1 {v24.8b - v27.8b}, [sp], 32
+ st1 {v28.8b - v31.8b}, [sp], 32
+ ld1 {v0.4h, v1.4h, v2.4h, v3.4h}, [TMP4]
+
+ /* Load all COEF_BLOCK into NEON registers with the following allocation:
+ * 0 1 2 3 | 4 5 6 7
+ * ---------+--------
+ * 0 | v4.4h | v5.4h
+ * 1 | v6.4h | v7.4h
+ * 2 | v8.4h | v9.4h
+ * 3 | v10.4h | v11.4h
+ * 4 | - | -
+ * 5 | v12.4h | v13.4h
+ * 6 | v14.4h | v15.4h
+ * 7 | v16.4h | v17.4h
+ */
+ ld1 {v4.4h, v5.4h, v6.4h, v7.4h}, [COEF_BLOCK], 32
+ ld1 {v8.4h, v9.4h, v10.4h, v11.4h}, [COEF_BLOCK], 32
+ add COEF_BLOCK, COEF_BLOCK, #16
+ ld1 {v12.4h, v13.4h, v14.4h, v15.4h}, [COEF_BLOCK], 32
+ ld1 {v16.4h, v17.4h}, [COEF_BLOCK], 16
+ /* dequantize */
+ ld1 {v18.4h, v19.4h, v20.4h, v21.4h}, [DCT_TABLE], 32
+ mul v4.4h, v4.4h, v18.4h
+ mul v5.4h, v5.4h, v19.4h
+ ins v4.2d[1], v5.2d[0] /* 128 bit q4 */
+ ld1 {v22.4h, v23.4h, v24.4h, v25.4h}, [DCT_TABLE], 32
+ mul v6.4h, v6.4h, v20.4h
+ mul v7.4h, v7.4h, v21.4h
+ ins v6.2d[1], v7.2d[0] /* 128 bit q6 */
+ mul v8.4h, v8.4h, v22.4h
+ mul v9.4h, v9.4h, v23.4h
+ ins v8.2d[1], v9.2d[0] /* 128 bit q8 */
+ add DCT_TABLE, DCT_TABLE, #16
+ ld1 {v26.4h, v27.4h, v28.4h, v29.4h}, [DCT_TABLE], 32
+ mul v10.4h, v10.4h, v24.4h
+ mul v11.4h, v11.4h, v25.4h
+ ins v10.2d[1], v11.2d[0] /* 128 bit q10 */
+ mul v12.4h, v12.4h, v26.4h
+ mul v13.4h, v13.4h, v27.4h
+ ins v12.2d[1], v13.2d[0] /* 128 bit q12 */
+ ld1 {v30.4h, v31.4h}, [DCT_TABLE], 16
+ mul v14.4h, v14.4h, v28.4h
+ mul v15.4h, v15.4h, v29.4h
+ ins v14.2d[1], v15.2d[0] /* 128 bit q14 */
+ mul v16.4h, v16.4h, v30.4h
+ mul v17.4h, v17.4h, v31.4h
+ ins v16.2d[1], v17.2d[0] /* 128 bit q16 */
+
+ /* Pass 1 */
+ idct_helper v4.4h, v6.4h, v8.4h, v10.4h, v12.4h, v14.4h, v16.4h, 12, v4.4h, v6.4h, v8.4h, v10.4h
+ transpose_4x4 v4, v6, v8, v10, v3
+ ins v10.2d[1], v11.2d[0]
+ idct_helper v5.4h, v7.4h, v9.4h, v11.4h, v13.4h, v15.4h, v17.4h, 12, v5.4h, v7.4h, v9.4h, v11.4h
+ transpose_4x4 v5, v7, v9, v11, v3
+ ins v10.2d[1], v11.2d[0]
+ /* Pass 2 */
+ idct_helper v4.4h, v6.4h, v8.4h, v10.4h, v7.4h, v9.4h, v11.4h, 19, v26.4h, v27.4h, v28.4h, v29.4h
+ transpose_4x4 v26, v27, v28, v29, v3
+
+ /* Range limit */
+ movi v30.8h, #0x80
+ ins v26.2d[1], v27.2d[0]
+ ins v28.2d[1], v29.2d[0]
+ add v26.8h, v26.8h, v30.8h
+ add v28.8h, v28.8h, v30.8h
+ sqxtun v26.8b, v26.8h
+ sqxtun v27.8b, v28.8h
+
+ /* Store results to the output buffer */
+ ldp TMP1, TMP2, [OUTPUT_BUF], 16
+ ldp TMP3, TMP4, [OUTPUT_BUF]
+ add TMP1, TMP1, OUTPUT_COL
+ add TMP2, TMP2, OUTPUT_COL
+ add TMP3, TMP3, OUTPUT_COL
+ add TMP4, TMP4, OUTPUT_COL
+
+#if defined(__ARMEL__) && !RESPECT_STRICT_ALIGNMENT
+ /* We can use much less instructions on little endian systems if the
+ * OS kernel is not configured to trap unaligned memory accesses
+ */
+ st1 {v26.s}[0], [TMP1], 4
+ st1 {v27.s}[0], [TMP3], 4
+ st1 {v26.s}[1], [TMP2], 4
+ st1 {v27.s}[1], [TMP4], 4
+#else
+ st1 {v26.b}[0], [TMP1], 1
+ st1 {v27.b}[0], [TMP3], 1
+ st1 {v26.b}[1], [TMP1], 1
+ st1 {v27.b}[1], [TMP3], 1
+ st1 {v26.b}[2], [TMP1], 1
+ st1 {v27.b}[2], [TMP3], 1
+ st1 {v26.b}[3], [TMP1], 1
+ st1 {v27.b}[3], [TMP3], 1
+
+ st1 {v26.b}[4], [TMP2], 1
+ st1 {v27.b}[4], [TMP4], 1
+ st1 {v26.b}[5], [TMP2], 1
+ st1 {v27.b}[5], [TMP4], 1
+ st1 {v26.b}[6], [TMP2], 1
+ st1 {v27.b}[6], [TMP4], 1
+ st1 {v26.b}[7], [TMP2], 1
+ st1 {v27.b}[7], [TMP4], 1
+#endif
+
+ /* vpop {v8.4h - v15.4h} ;not available */
+ sub sp, sp, #272
+ ldr x15, [sp], 16
+ ld1 {v0.8b - v3.8b}, [sp], 32
+ ld1 {v4.8b - v7.8b}, [sp], 32
+ ld1 {v8.8b - v11.8b}, [sp], 32
+ ld1 {v12.8b - v15.8b}, [sp], 32
+ ld1 {v16.8b - v19.8b}, [sp], 32
+ ld1 {v20.8b - v23.8b}, [sp], 32
+ ld1 {v24.8b - v27.8b}, [sp], 32
+ ld1 {v28.8b - v31.8b}, [sp], 32
+ blr x30
+
+ .unreq DCT_TABLE
+ .unreq COEF_BLOCK
+ .unreq OUTPUT_BUF
+ .unreq OUTPUT_COL
+ .unreq TMP1
+ .unreq TMP2
+ .unreq TMP3
+ .unreq TMP4
+
+.purgem idct_helper
+
+
+/*****************************************************************************/
+
+/*
+ * jsimd_idct_2x2_neon
+ *
+ * This function contains inverse-DCT code for getting reduced-size
+ * 2x2 pixels output from an 8x8 DCT block. It uses the same calculations
+ * and produces exactly the same output as IJG's original 'jpeg_idct_2x2'
+ * function from jpeg-6b (jidctred.c).
+ *
+ * NOTE: jpeg-8 has an improved implementation of 2x2 inverse-DCT, which
+ * requires much less arithmetic operations and hence should be faster.
+ * The primary purpose of this particular NEON optimized function is
+ * bit exact compatibility with jpeg-6b.
+ */
+
+.balign 8
+jsimd_idct_2x2_neon_consts:
+ .short -FIX_0_720959822 /* v14[0] */
+ .short FIX_0_850430095 /* v14[1] */
+ .short -FIX_1_272758580 /* v14[2] */
+ .short FIX_3_624509785 /* v14[3] */
+
+.macro idct_helper x4, x6, x10, x12, x16, shift, y26, y27
+ sshll v15.4s, \x4, #15
+ smull v26.4s, \x6, v14.4h[3]
+ smlal v26.4s, \x10, v14.4h[2]
+ smlal v26.4s, \x12, v14.4h[1]
+ smlal v26.4s, \x16, v14.4h[0]
+
+ add v20.4s, v15.4s, v26.4s
+ sub v15.4s, v15.4s, v26.4s
+
+.if \shift > 16
+ srshr v20.4s, v20.4s, #\shift
+ srshr v15.4s, v15.4s, #\shift
+ xtn \y26, v20.4s
+ xtn \y27, v15.4s
+.else
+ rshrn \y26, v20.4s, #\shift
+ rshrn \y27, v15.4s, #\shift
+.endif
+
+.endm
+
+asm_function jsimd_idct_2x2_neon
+
+ DCT_TABLE .req x0
+ COEF_BLOCK .req x1
+ OUTPUT_BUF .req x2
+ OUTPUT_COL .req x3
+ TMP1 .req x0
+ TMP2 .req x15
+
+ /* vpush {v8.4h - v15.4h} ; not available */
+ sub sp, sp, 208
+ str x15, [sp], 16
+
+ /* Load constants */
+ adr TMP2, jsimd_idct_2x2_neon_consts
+ st1 {v4.8b - v7.8b}, [sp], 32
+ st1 {v8.8b - v11.8b}, [sp], 32
+ st1 {v12.8b - v15.8b}, [sp], 32
+ st1 {v16.8b - v19.8b}, [sp], 32
+ st1 {v21.8b - v22.8b}, [sp], 16
+ st1 {v24.8b - v27.8b}, [sp], 32
+ st1 {v30.8b - v31.8b}, [sp], 16
+ ld1 {v14.4h}, [TMP2]
+
+ /* Load all COEF_BLOCK into NEON registers with the following allocation:
+ * 0 1 2 3 | 4 5 6 7
+ * ---------+--------
+ * 0 | v4.4h | v5.4h
+ * 1 | v6.4h | v7.4h
+ * 2 | - | -
+ * 3 | v10.4h | v11.4h
+ * 4 | - | -
+ * 5 | v12.4h | v13.4h
+ * 6 | - | -
+ * 7 | v16.4h | v17.4h
+ */
+ ld1 {v4.4h, v5.4h, v6.4h, v7.4h}, [COEF_BLOCK], 32
+ add COEF_BLOCK, COEF_BLOCK, #16
+ ld1 {v10.4h, v11.4h}, [COEF_BLOCK], 16
+ add COEF_BLOCK, COEF_BLOCK, #16
+ ld1 {v12.4h, v13.4h}, [COEF_BLOCK], 16
+ add COEF_BLOCK, COEF_BLOCK, #16
+ ld1 {v16.4h, v17.4h}, [COEF_BLOCK], 16
+ /* Dequantize */
+ ld1 {v18.4h, v19.4h, v20.4h, v21.4h}, [DCT_TABLE], 32
+ mul v4.4h, v4.4h, v18.4h
+ mul v5.4h, v5.4h, v19.4h
+ ins v4.2d[1], v5.2d[0]
+ mul v6.4h, v6.4h, v20.4h
+ mul v7.4h, v7.4h, v21.4h
+ ins v6.2d[1], v7.2d[0]
+ add DCT_TABLE, DCT_TABLE, #16
+ ld1 {v24.4h, v25.4h}, [DCT_TABLE], 16
+ mul v10.4h, v10.4h, v24.4h
+ mul v11.4h, v11.4h, v25.4h
+ ins v10.2d[1], v11.2d[0]
+ add DCT_TABLE, DCT_TABLE, #16
+ ld1 {v26.4h, v27.4h}, [DCT_TABLE], 16
+ mul v12.4h, v12.4h, v26.4h
+ mul v13.4h, v13.4h, v27.4h
+ ins v12.2d[1], v13.2d[0]
+ add DCT_TABLE, DCT_TABLE, #16
+ ld1 {v30.4h, v31.4h}, [DCT_TABLE], 16
+ mul v16.4h, v16.4h, v30.4h
+ mul v17.4h, v17.4h, v31.4h
+ ins v16.2d[1], v17.2d[0]
+
+ /* Pass 1 */
+#if 0
+ idct_helper v4.4h, v6.4h, v10.4h, v12.4h, v16.4h, 13, v4.4h, v6.4h
+ transpose_4x4 v4.4h, v6.4h, v8.4h, v10.4h
+ idct_helper v5.4h, v7.4h, v11.4h, v13.4h, v17.4h, 13, v5.4h, v7.4h
+ transpose_4x4 v5.4h, v7.4h, v9.4h, v11.4h
+#else
+ smull v26.4s, v6.4h, v14.4h[3]
+ smlal v26.4s, v10.4h, v14.4h[2]
+ smlal v26.4s, v12.4h, v14.4h[1]
+ smlal v26.4s, v16.4h, v14.4h[0]
+ smull v24.4s, v7.4h, v14.4h[3]
+ smlal v24.4s, v11.4h, v14.4h[2]
+ smlal v24.4s, v13.4h, v14.4h[1]
+ smlal v24.4s, v17.4h, v14.4h[0]
+ sshll v15.4s, v4.4h, #15
+ sshll v30.4s, v5.4h, #15
+ add v20.4s, v15.4s, v26.4s
+ sub v15.4s, v15.4s, v26.4s
+ rshrn v4.4h, v20.4s, #13
+ rshrn v6.4h, v15.4s, #13
+ add v20.4s, v30.4s, v24.4s
+ sub v15.4s, v30.4s, v24.4s
+ rshrn v5.4h, v20.4s, #13
+ rshrn v7.4h, v15.4s, #13
+ ins v4.2d[1], v5.2d[0]
+ ins v6.2d[1], v7.2d[0]
+ transpose v4, v6, v3, .16b, .8h
+ transpose v6, v10, v3, .16b, .4s
+ ins v11.2d[0], v10.2d[1]
+ ins v7.2d[0], v6.2d[1]
+#endif
+
+ /* Pass 2 */
+ idct_helper v4.4h, v6.4h, v10.4h, v7.4h, v11.4h, 20, v26.4h, v27.4h
+
+ /* Range limit */
+ movi v30.8h, #0x80
+ ins v26.2d[1], v27.2d[0]
+ add v26.8h, v26.8h, v30.8h
+ sqxtun v30.8b, v26.8h
+ ins v26.2d[0], v30.2d[0]
+ sqxtun v27.8b, v26.8h
+
+ /* Store results to the output buffer */
+ ldp TMP1, TMP2, [OUTPUT_BUF]
+ add TMP1, TMP1, OUTPUT_COL
+ add TMP2, TMP2, OUTPUT_COL
+
+ st1 {v26.b}[0], [TMP1], 1
+ st1 {v27.b}[4], [TMP1], 1
+ st1 {v26.b}[1], [TMP2], 1
+ st1 {v27.b}[5], [TMP2], 1
+
+ sub sp, sp, #208
+ ldr x15, [sp], 16
+ ld1 {v4.8b - v7.8b}, [sp], 32
+ ld1 {v8.8b - v11.8b}, [sp], 32
+ ld1 {v12.8b - v15.8b}, [sp], 32
+ ld1 {v16.8b - v19.8b}, [sp], 32
+ ld1 {v21.8b - v22.8b}, [sp], 16
+ ld1 {v24.8b - v27.8b}, [sp], 32
+ ld1 {v30.8b - v31.8b}, [sp], 16
+ blr x30
+
+ .unreq DCT_TABLE
+ .unreq COEF_BLOCK
+ .unreq OUTPUT_BUF
+ .unreq OUTPUT_COL
+ .unreq TMP1
+ .unreq TMP2
+
+.purgem idct_helper
+
+
+/*****************************************************************************/
+
+/*
+ * jsimd_ycc_extrgb_convert_neon
+ * jsimd_ycc_extbgr_convert_neon
+ * jsimd_ycc_extrgbx_convert_neon
+ * jsimd_ycc_extbgrx_convert_neon
+ * jsimd_ycc_extxbgr_convert_neon
+ * jsimd_ycc_extxrgb_convert_neon
+ *
+ * Colorspace conversion YCbCr -> RGB
+ */
+
+
+.macro do_load size
+ .if \size == 8
+ ld1 {v4.8b}, [U], 8
+ ld1 {v5.8b}, [V], 8
+ ld1 {v0.8b}, [Y], 8
+ prfm PLDL1KEEP, [U, #64]
+ prfm PLDL1KEEP, [V, #64]
+ prfm PLDL1KEEP, [Y, #64]
+ .elseif \size == 4
+ ld1 {v4.b}[0], [U], 1
+ ld1 {v4.b}[1], [U], 1
+ ld1 {v4.b}[2], [U], 1
+ ld1 {v4.b}[3], [U], 1
+ ld1 {v5.b}[0], [V], 1
+ ld1 {v5.b}[1], [V], 1
+ ld1 {v5.b}[2], [V], 1
+ ld1 {v5.b}[3], [V], 1
+ ld1 {v0.b}[0], [Y], 1
+ ld1 {v0.b}[1], [Y], 1
+ ld1 {v0.b}[2], [Y], 1
+ ld1 {v0.b}[3], [Y], 1
+ .elseif \size == 2
+ ld1 {v4.b}[4], [U], 1
+ ld1 {v4.b}[5], [U], 1
+ ld1 {v5.b}[4], [V], 1
+ ld1 {v5.b}[5], [V], 1
+ ld1 {v0.b}[4], [Y], 1
+ ld1 {v0.b}[5], [Y], 1
+ .elseif \size == 1
+ ld1 {v4.b}[6], [U], 1
+ ld1 {v5.b}[6], [V], 1
+ ld1 {v0.b}[6], [Y], 1
+ .else
+ .error unsupported macroblock size
+ .endif
+.endm
+
+.macro do_store bpp, size
+ .if \bpp == 24
+ .if \size == 8
+ st3 {v10.8b, v11.8b, v12.8b}, [RGB], 24
+ .elseif \size == 4
+ st3 {v10.b, v11.b, v12.b}[0], [RGB], 3
+ st3 {v10.b, v11.b, v12.b}[1], [RGB], 3
+ st3 {v10.b, v11.b, v12.b}[2], [RGB], 3
+ st3 {v10.b, v11.b, v12.b}[3], [RGB], 3
+ .elseif \size == 2
+ st3 {v10.b, v11.b, v12.b}[4], [RGB], 3
+ st3 {v10.b, v11.b, v12.b}[5], [RGB], 3
+ .elseif \size == 1
+ st3 {v10.b, v11.b, v12.b}[6], [RGB], 3
+ .else
+ .error unsupported macroblock size
+ .endif
+ .elseif \bpp == 32
+ .if \size == 8
+ st4 {v10.8b, v11.8b, v12.8b, v13.8b}, [RGB], 32
+ .elseif \size == 4
+ st4 {v10.b, v11.b, v12.b, v13.b}[0], [RGB], 4
+ st4 {v10.b, v11.b, v12.b, v13.b}[1], [RGB], 4
+ st4 {v10.b, v11.b, v12.b, v13.b}[2], [RGB], 4
+ st4 {v10.b, v11.b, v12.b, v13.b}[3], [RGB], 4
+ .elseif \size == 2
+ st4 {v10.b, v11.b, v12.b, v13.b}[4], [RGB], 4
+ st4 {v10.b, v11.b, v12.b, v13.b}[5], [RGB], 4
+ .elseif \size == 1
+ st4 {v10.b, v11.b, v12.b, v13.b}[6], [RGB], 4
+ .else
+ .error unsupported macroblock size
+ .endif
+ .elseif \bpp==16
+ .if \size == 8
+ st1 {v25.8h}, [RGB],16
+ .elseif \size == 4
+ st1 {v25.4h}, [RGB],8
+ .elseif \size == 2
+ st1 {v25.h}[4], [RGB],2
+ st1 {v25.h}[5], [RGB],2
+ .elseif \size == 1
+ st1 {v25.h}[6], [RGB],2
+ .else
+ .error unsupported macroblock size
+ .endif
+ .else
+ .error unsupported bpp
+ .endif
+.endm
+
+.macro generate_jsimd_ycc_rgb_convert_neon colorid, bpp, r_offs, rsize, g_offs, gsize, b_offs, bsize, defsize
+
+/*
+ * 2-stage pipelined YCbCr->RGB conversion
+ */
+
+.macro do_yuv_to_rgb_stage1
+ uaddw v6.8h, v2.8h, v4.8b /* q3 = u - 128 */
+ uaddw v8.8h, v2.8h, v5.8b /* q2 = v - 128 */
+ smull v20.4s, v6.4h, v1.4h[1] /* multiply by -11277 */
+ smlal v20.4s, v8.4h, v1.4h[2] /* multiply by -23401 */
+ smull2 v22.4s, v6.8h, v1.4h[1] /* multiply by -11277 */
+ smlal2 v22.4s, v8.8h, v1.4h[2] /* multiply by -23401 */
+ smull v24.4s, v8.4h, v1.4h[0] /* multiply by 22971 */
+ smull2 v26.4s, v8.8h, v1.4h[0] /* multiply by 22971 */
+ smull v28.4s, v6.4h, v1.4h[3] /* multiply by 29033 */
+ smull2 v30.4s, v6.8h, v1.4h[3] /* multiply by 29033 */
+.endm
+
+.macro do_yuv_to_rgb_stage2
+ rshrn v20.4h, v20.4s, #15
+ rshrn2 v20.8h, v22.4s, #15
+ rshrn v24.4h, v24.4s, #14
+ rshrn2 v24.8h, v26.4s, #14
+ rshrn v28.4h, v28.4s, #14
+ rshrn2 v28.8h, v30.4s, #14
+ uaddw v20.8h, v20.8h, v0.8b
+ uaddw v24.8h, v24.8h, v0.8b
+ uaddw v28.8h, v28.8h, v0.8b
+.if \bpp != 16
+ sqxtun v1\g_offs\defsize, v20.8h
+ sqxtun v1\r_offs\defsize, v24.8h
+ sqxtun v1\b_offs\defsize, v28.8h
+.else
+ sqshlu v21.8h, v20.8h, #8
+ sqshlu v25.8h, v24.8h, #8
+ sqshlu v29.8h, v28.8h, #8
+ sri v25.8h, v21.8h, #5
+ sri v25.8h, v29.8h, #11
+.endif
+
+.endm
+
+.macro do_yuv_to_rgb_stage2_store_load_stage1
+ rshrn v20.4h, v20.4s, #15
+ rshrn v24.4h, v24.4s, #14
+ rshrn v28.4h, v28.4s, #14
+ ld1 {v4.8b}, [U], 8
+ rshrn2 v20.8h, v22.4s, #15
+ rshrn2 v24.8h, v26.4s, #14
+ rshrn2 v28.8h, v30.4s, #14
+ ld1 {v5.8b}, [V], 8
+ uaddw v20.8h, v20.8h, v0.8b
+ uaddw v24.8h, v24.8h, v0.8b
+ uaddw v28.8h, v28.8h, v0.8b
+.if \bpp != 16 /**************** rgb24/rgb32 *********************************/
+ sqxtun v1\g_offs\defsize, v20.8h
+ ld1 {v0.8b}, [Y], 8
+ sqxtun v1\r_offs\defsize, v24.8h
+ prfm PLDL1KEEP, [U, #64]
+ prfm PLDL1KEEP, [V, #64]
+ prfm PLDL1KEEP, [Y, #64]
+ sqxtun v1\b_offs\defsize, v28.8h
+ uaddw v6.8h, v2.8h, v4.8b /* v6.16b = u - 128 */
+ uaddw v8.8h, v2.8h, v5.8b /* q2 = v - 128 */
+ smull v20.4s, v6.4h, v1.4h[1] /* multiply by -11277 */
+ smlal v20.4s, v8.4h, v1.4h[2] /* multiply by -23401 */
+ smull2 v22.4s, v6.8h, v1.4h[1] /* multiply by -11277 */
+ smlal2 v22.4s, v8.8h, v1.4h[2] /* multiply by -23401 */
+ smull v24.4s, v8.4h, v1.4h[0] /* multiply by 22971 */
+ smull2 v26.4s, v8.8h, v1.4h[0] /* multiply by 22971 */
+.else /**************************** rgb565 ***********************************/
+ sqshlu v21.8h, v20.8h, #8
+ sqshlu v25.8h, v24.8h, #8
+ sqshlu v29.8h, v28.8h, #8
+ uaddw v6.8h, v2.8h, v4.8b /* v6.16b = u - 128 */
+ uaddw v8.8h, v2.8h, v5.8b /* q2 = v - 128 */
+ ld1 {v0.8b}, [Y], 8
+ smull v20.4s, v6.4h, v1.4h[1] /* multiply by -11277 */
+ smlal v20.4s, v8.4h, v1.4h[2] /* multiply by -23401 */
+ smull2 v22.4s, v6.8h, v1.4h[1] /* multiply by -11277 */
+ smlal2 v22.4s, v8.8h, v1.4h[2] /* multiply by -23401 */
+ sri v25.8h, v21.8h, #5
+ smull v24.4s, v8.4h, v1.4h[0] /* multiply by 22971 */
+ smull2 v26.4s, v8.8h, v1.4h[0] /* multiply by 22971 */
+ prfm PLDL1KEEP, [U, #64]
+ prfm PLDL1KEEP, [V, #64]
+ prfm PLDL1KEEP, [Y, #64]
+ sri v25.8h, v29.8h, #11
+.endif
+ do_store \bpp, 8
+ smull v28.4s, v6.4h, v1.4h[3] /* multiply by 29033 */
+ smull2 v30.4s, v6.8h, v1.4h[3] /* multiply by 29033 */
+.endm
+
+.macro do_yuv_to_rgb
+ do_yuv_to_rgb_stage1
+ do_yuv_to_rgb_stage2
+.endm
+
+/* Apple gas crashes on adrl, work around that by using adr.
+ * But this requires a copy of these constants for each function.
+ */
+
+.balign 16
+jsimd_ycc_\colorid\()_neon_consts:
+ .short 0, 0, 0, 0
+ .short 22971, -11277, -23401, 29033
+ .short -128, -128, -128, -128
+ .short -128, -128, -128, -128
+
+asm_function jsimd_ycc_\colorid\()_convert_neon
+ OUTPUT_WIDTH .req x0
+ INPUT_BUF .req x1
+ INPUT_ROW .req x2
+ OUTPUT_BUF .req x3
+ NUM_ROWS .req x4
+
+ INPUT_BUF0 .req x5
+ INPUT_BUF1 .req x6
+ INPUT_BUF2 .req INPUT_BUF
+
+ RGB .req x7
+ Y .req x8
+ U .req x9
+ V .req x10
+ N .req x15
+
+ sub sp, sp, 336
+ str x15, [sp], 16
+ /* Load constants to d1, d2, d3 (v0.4h is just used for padding) */
+ adr x15, jsimd_ycc_\colorid\()_neon_consts
+ /* Save NEON registers */
+ st1 {v0.8b - v3.8b}, [sp], 32
+ st1 {v4.8b - v7.8b}, [sp], 32
+ st1 {v8.8b - v11.8b}, [sp], 32
+ st1 {v12.8b - v15.8b}, [sp], 32
+ st1 {v16.8b - v19.8b}, [sp], 32
+ st1 {v20.8b - v23.8b}, [sp], 32
+ st1 {v24.8b - v27.8b}, [sp], 32
+ st1 {v28.8b - v31.8b}, [sp], 32
+ ld1 {v0.4h, v1.4h}, [x15], 16
+ ld1 {v2.8h}, [x15]
+
+ /* Save ARM registers and handle input arguments */
+ /* push {x4, x5, x6, x7, x8, x9, x10, x30} */
+ stp x4, x5, [sp], 16
+ stp x6, x7, [sp], 16
+ stp x8, x9, [sp], 16
+ stp x10, x30, [sp], 16
+ ldr INPUT_BUF0, [INPUT_BUF]
+ ldr INPUT_BUF1, [INPUT_BUF, 8]
+ ldr INPUT_BUF2, [INPUT_BUF, 16]
+ .unreq INPUT_BUF
+
+ /* Initially set v10, v11.4h, v12.8b, d13 to 0xFF */
+ movi v10.16b, #255
+ movi v13.16b, #255
+
+ /* Outer loop over scanlines */
+ cmp NUM_ROWS, #1
+ blt 9f
+0:
+ lsl x16, INPUT_ROW, #3
+ ldr Y, [INPUT_BUF0, x16]
+ ldr U, [INPUT_BUF1, x16]
+ mov N, OUTPUT_WIDTH
+ ldr V, [INPUT_BUF2, x16]
+ add INPUT_ROW, INPUT_ROW, #1
+ ldr RGB, [OUTPUT_BUF], #8
+
+ /* Inner loop over pixels */
+ subs N, N, #8
+ blt 3f
+ do_load 8
+ do_yuv_to_rgb_stage1
+ subs N, N, #8
+ blt 2f
+1:
+ do_yuv_to_rgb_stage2_store_load_stage1
+ subs N, N, #8
+ bge 1b
+2:
+ do_yuv_to_rgb_stage2
+ do_store \bpp, 8
+ tst N, #7
+ beq 8f
+3:
+ tst N, #4
+ beq 3f
+ do_load 4
+3:
+ tst N, #2
+ beq 4f
+ do_load 2
+4:
+ tst N, #1
+ beq 5f
+ do_load 1
+5:
+ do_yuv_to_rgb
+ tst N, #4
+ beq 6f
+ do_store \bpp, 4
+6:
+ tst N, #2
+ beq 7f
+ do_store \bpp, 2
+7:
+ tst N, #1
+ beq 8f
+ do_store \bpp, 1
+8:
+ subs NUM_ROWS, NUM_ROWS, #1
+ bgt 0b
+9:
+ /* Restore all registers and return */
+ sub sp, sp, #336
+ ldr x15, [sp], 16
+ ld1 {v0.8b - v3.8b}, [sp], 32
+ ld1 {v4.8b - v7.8b}, [sp], 32
+ ld1 {v8.8b - v11.8b}, [sp], 32
+ ld1 {v12.8b - v15.8b}, [sp], 32
+ ld1 {v16.8b - v19.8b}, [sp], 32
+ ld1 {v20.8b - v23.8b}, [sp], 32
+ ld1 {v24.8b - v27.8b}, [sp], 32
+ ld1 {v28.8b - v31.8b}, [sp], 32
+ /* pop {r4, r5, r6, r7, r8, r9, r10, pc} */
+ ldp x4, x5, [sp], 16
+ ldp x6, x7, [sp], 16
+ ldp x8, x9, [sp], 16
+ ldp x10, x30, [sp], 16
+ br x30
+ .unreq OUTPUT_WIDTH
+ .unreq INPUT_ROW
+ .unreq OUTPUT_BUF
+ .unreq NUM_ROWS
+ .unreq INPUT_BUF0
+ .unreq INPUT_BUF1
+ .unreq INPUT_BUF2
+ .unreq RGB
+ .unreq Y
+ .unreq U
+ .unreq V
+ .unreq N
+
+.purgem do_yuv_to_rgb
+.purgem do_yuv_to_rgb_stage1
+.purgem do_yuv_to_rgb_stage2
+.purgem do_yuv_to_rgb_stage2_store_load_stage1
+.endm
+
+/*--------------------------------- id ----- bpp R rsize G gsize B bsize defsize */
+generate_jsimd_ycc_rgb_convert_neon extrgb, 24, 0, .4h, 1, .4h, 2, .4h, .8b
+generate_jsimd_ycc_rgb_convert_neon extbgr, 24, 2, .4h, 1, .4h, 0, .4h, .8b
+generate_jsimd_ycc_rgb_convert_neon extrgbx, 32, 0, .4h, 1, .4h, 2, .4h, .8b
+generate_jsimd_ycc_rgb_convert_neon extbgrx, 32, 2, .4h, 1, .4h, 0, .4h, .8b
+generate_jsimd_ycc_rgb_convert_neon extxbgr, 32, 3, .4h, 2, .4h, 1, .4h, .8b
+generate_jsimd_ycc_rgb_convert_neon extxrgb, 32, 1, .4h, 2, .4h, 3, .4h, .8b
+generate_jsimd_ycc_rgb_convert_neon rgb565, 16, 0, .4h, 0, .4h, 0, .4h, .8b
+.purgem do_load
+.purgem do_store
/* Supplementary macro for setting function attributes */
.macro asm_function fname
#ifdef __APPLE__
- .func _\fname
.globl _\fname
_\fname:
#else
- .func \fname
.global \fname
#ifdef __ELF__
.hidden \fname
.unreq ROW6R
.unreq ROW7L
.unreq ROW7R
-.endfunc
/*****************************************************************************/
.unreq TMP2
.unreq TMP3
.unreq TMP4
-.endfunc
/*****************************************************************************/
.unreq TMP2
.unreq TMP3
.unreq TMP4
-.endfunc
.purgem idct_helper
.unreq OUTPUT_COL
.unreq TMP1
.unreq TMP2
-.endfunc
.purgem idct_helper
.unreq U
.unreq V
.unreq N
-.endfunc
.purgem do_yuv_to_rgb
.purgem do_yuv_to_rgb_stage1
.unreq U
.unreq V
.unreq N
-.endfunc
.purgem do_rgb_to_yuv
.purgem do_rgb_to_yuv_stage1
.unreq TMP2
.unreq TMP3
.unreq TMP4
-.endfunc
/*****************************************************************************/
.unreq DATA
.unreq TMP
-.endfunc
/*****************************************************************************/
.unreq CORRECTION
.unreq SHIFT
.unreq LOOP_COUNT
-.endfunc
/*****************************************************************************/
.unreq WIDTH
.unreq TMP
-.endfunc
.purgem upsample16
.purgem upsample32
cm->mi_cols = aligned_width >> MI_SIZE_LOG2;
cm->mi_rows = aligned_height >> MI_SIZE_LOG2;
- cm->mi_stride = cm->mi_cols + MI_BLOCK_SIZE;
+ cm->mi_stride = calc_mi_size(cm->mi_cols);
cm->mb_cols = (cm->mi_cols + 1) >> 1;
cm->mb_rows = (cm->mi_rows + 1) >> 1;
for (i = 0; i < 2; ++i) {
cm->mip_array[i] =
- (MODE_INFO *)vpx_calloc(mi_size, sizeof(*cm->mip));
+ (MODE_INFO *)vpx_calloc(mi_size, sizeof(MODE_INFO));
if (cm->mip_array[i] == NULL)
return 1;
cm->mi_grid_base_array[i] =
- (MODE_INFO **)vpx_calloc(mi_size, sizeof(*cm->mi_grid_base));
+ (MODE_INFO **)vpx_calloc(mi_size, sizeof(MODE_INFO*));
if (cm->mi_grid_base_array[i] == NULL)
return 1;
}
+ cm->mi_alloc_size = mi_size;
+
// Init the index.
cm->mi_idx = 0;
cm->prev_mi_idx = 1;
vp9_free_context_buffers(cm);
vp9_set_mb_mi(cm, width, height);
- if (alloc_mi(cm, cm->mi_stride * (cm->mi_rows + MI_BLOCK_SIZE))) goto fail;
+ if (alloc_mi(cm, cm->mi_stride * calc_mi_size(cm->mi_rows)))
+ goto fail;
cm->last_frame_seg_map = (uint8_t *)vpx_calloc(cm->mi_rows * cm->mi_cols, 1);
if (!cm->last_frame_seg_map) goto fail;
int mi_idx;
int prev_mi_idx;
+ int mi_alloc_size;
MODE_INFO *mip_array[2];
MODE_INFO **mi_grid_base_array[2];
}
}
+static INLINE int calc_mi_size(int len) {
+ // len is in mi units.
+ return len + MI_BLOCK_SIZE;
+}
+
static INLINE void set_mi_row_col(MACROBLOCKD *xd, const TileInfo *const tile,
int mi_row, int bh,
int mi_col, int bw,
"Width and height beyond allowed size.");
#endif
if (cm->width != width || cm->height != height) {
+ const int aligned_width = ALIGN_POWER_OF_TWO(width, MI_SIZE_LOG2);
+ const int aligned_height = ALIGN_POWER_OF_TWO(height, MI_SIZE_LOG2);
+
// Change in frame size (assumption: color format does not change).
if (cm->width == 0 || cm->height == 0 ||
- width * height > cm->width * cm->height) {
+ aligned_width > cm->width ||
+ aligned_width * aligned_height > cm->width * cm->height) {
if (vp9_alloc_context_buffers(cm, width, height))
vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
- "Failed to allocate frame buffers");
+ "Failed to allocate context buffers");
} else {
vp9_set_mb_mi(cm, width, height);
}
const int bs = (1 << bsl) / 4;
PARTITION_TYPE partition;
BLOCK_SIZE subsize;
- MODE_INFO *m = cm->mi_grid_visible[mi_row * cm->mi_stride + mi_col];
+ const MODE_INFO *m = NULL;
if (mi_row >= cm->mi_rows || mi_col >= cm->mi_cols)
return;
+ m = cm->mi_grid_visible[mi_row * cm->mi_stride + mi_col];
+
partition = partition_lookup[bsl][m->mbmi.sb_type];
write_partition(cm, xd, bs, mi_row, mi_col, partition, bsize, w);
subsize = get_subsize(bsize, partition);
(NULL != paint->getColorFilter()));
SkRect drawBounds;
if (paintAffectsTransparentBlack) {
- if (bounds) {
- drawBounds = *bounds;
- this->getTotalMatrix().mapRect(&drawBounds);
- } else {
- SkIRect deviceBounds;
- this->getClipDeviceBounds(&deviceBounds);
- drawBounds.set(deviceBounds);
- }
+ SkIRect deviceBounds;
+ this->getClipDeviceBounds(&deviceBounds);
+ drawBounds.set(deviceBounds);
}
fStateTree->appendSaveLayer(this->writeStream().bytesWritten());
SkCanvas::SaveLayerStrategy strategy = this->INHERITED::willSaveLayer(bounds, paint, flags);
void* fDistanceField;
uint8_t fMaskFormat;
int8_t fRsbDelta, fLsbDelta; // used by auto-kerning
+ int8_t fForceBW;
void init(uint32_t id) {
fID = id;
fPath = NULL;
fDistanceField = NULL;
fMaskFormat = MASK_FORMAT_UNKNOWN;
+ fForceBW = 0;
}
/**
bool degenerateBC = SkPath::IsLineDegenerate(pt1, pt2);
bool degenerateCD = SkPath::IsLineDegenerate(pt2, pt3);
- if (degenerateAB + degenerateBC + degenerateCD >= 2) {
+ if (degenerateAB + degenerateBC + degenerateCD >= 2
+ || (degenerateAB && SkPath::IsLineDegenerate(fPrevPt, pt2))) {
this->lineTo(pt3);
return;
}
return NULL;
}
+// if one is very large the smaller may have collapsed to nothing
+static void bump_out_close_span(double* startTPtr, double* endTPtr) {
+ double startT = *startTPtr;
+ double endT = *endTPtr;
+ if (approximately_negative(endT - startT)) {
+ if (endT <= 1 - FLT_EPSILON) {
+ *endTPtr += FLT_EPSILON;
+ SkASSERT(*endTPtr <= 1);
+ } else {
+ *startTPtr -= FLT_EPSILON;
+ SkASSERT(*startTPtr >= 0);
+ }
+ }
+}
+
// first pass, add missing T values
// second pass, determine winding values of overlaps
void SkOpContour::addCoincidentPoints() {
if ((cancelers = startSwapped = startT > endT)) {
SkTSwap(startT, endT);
}
- if (startT == endT) { // if one is very large the smaller may have collapsed to nothing
- if (endT <= 1 - FLT_EPSILON) {
- endT += FLT_EPSILON;
- SkASSERT(endT <= 1);
- } else {
- startT -= FLT_EPSILON;
- SkASSERT(startT >= 0);
- }
- }
+ bump_out_close_span(&startT, &endT);
SkASSERT(!approximately_negative(endT - startT));
double oStartT = coincidence.fTs[1][0];
double oEndT = coincidence.fTs[1][1];
SkTSwap(oStartT, oEndT);
cancelers ^= true;
}
+ bump_out_close_span(&oStartT, &oEndT);
SkASSERT(!approximately_negative(oEndT - oStartT));
const SkPoint& startPt = coincidence.fPts[0][startSwapped];
if (cancelers) {
SkTSwap<double>(startT, endT);
SkTSwap<const SkPoint*>(startPt, endPt);
}
- if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
- if (endT <= 1 - FLT_EPSILON) {
- endT += FLT_EPSILON;
- SkASSERT(endT <= 1);
- } else {
- startT -= FLT_EPSILON;
- SkASSERT(startT >= 0);
- }
- }
+ bump_out_close_span(&startT, &endT);
SkASSERT(!approximately_negative(endT - startT));
double oStartT = coincidence.fTs[1][0];
double oEndT = coincidence.fTs[1][1];
SkTSwap<double>(oStartT, oEndT);
cancelers ^= true;
}
+ bump_out_close_span(&oStartT, &oEndT);
SkASSERT(!approximately_negative(oEndT - oStartT));
if (cancelers) {
thisOne.addTCancel(*startPt, *endPt, &other);
double testT = test->fT;
SkOpSpan* oTest = &other->fTs[oIndex];
const SkPoint* oTestPt = &oTest->fPt;
- SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
+ // paths with extreme data will fail this test and eject out of pathops altogether later on
+ // SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
do {
SkASSERT(test->fT < 1);
SkASSERT(oTest->fT < 1);
const SkOpSpan* span = &fTs[0];
if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) {
index = findStartSpan(0); // curve start intersects
+ if (fTs[index].fT == 0) {
+ return false;
+ }
SkASSERT(index > 0);
if (activePrior >= 0) {
addStartSpan(index);
glyph->fAdvanceY = SkScalarToFixed(vecs[0].fY);
}
-void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) {
- glyph->fWidth = 0;
-
- this->generateAdvance(glyph);
-
+HRESULT SkScalerContext_DW::getBoundingBox(SkGlyph* glyph,
+ DWRITE_RENDERING_MODE renderingMode,
+ DWRITE_TEXTURE_TYPE textureType,
+ RECT* bbox)
+{
//Measure raster size.
fXform.dx = SkFixedToFloat(glyph->getSubXFixed());
fXform.dy = SkFixedToFloat(glyph->getSubYFixed());
run.glyphOffsets = &offset;
SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
- HRVM(fTypeface->fFactory->CreateGlyphRunAnalysis(
- &run,
- 1.0f, // pixelsPerDip,
- &fXform,
- fRenderingMode,
- fMeasuringMode,
- 0.0f, // baselineOriginX,
- 0.0f, // baselineOriginY,
- &glyphRunAnalysis),
- "Could not create glyph run analysis.");
-
- RECT bbox;
- HRVM(glyphRunAnalysis->GetAlphaTextureBounds(fTextureType, &bbox),
- "Could not get texture bounds.");
+ HRM(fTypeface->fFactory->CreateGlyphRunAnalysis(
+ &run,
+ 1.0f, // pixelsPerDip,
+ &fXform,
+ renderingMode,
+ fMeasuringMode,
+ 0.0f, // baselineOriginX,
+ 0.0f, // baselineOriginY,
+ &glyphRunAnalysis),
+ "Could not create glyph run analysis.");
+
+ HRM(glyphRunAnalysis->GetAlphaTextureBounds(textureType, bbox),
+ "Could not get texture bounds.");
+
+ return S_OK;
+}
+/** GetAlphaTextureBounds succeeds but sometimes returns empty bounds like
+ * { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }
+ * for small, but not quite zero, sized glyphs.
+ * Only set as non-empty if the returned bounds are non-empty.
+ */
+static bool glyph_check_and_set_bounds(SkGlyph* glyph, const RECT& bbox) {
+ if (bbox.left >= bbox.right || bbox.top >= bbox.bottom) {
+ return false;
+ }
glyph->fWidth = SkToU16(bbox.right - bbox.left);
glyph->fHeight = SkToU16(bbox.bottom - bbox.top);
glyph->fLeft = SkToS16(bbox.left);
glyph->fTop = SkToS16(bbox.top);
+ return true;
+}
+
+void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) {
+ glyph->fWidth = 0;
+ glyph->fHeight = 0;
+ glyph->fLeft = 0;
+ glyph->fTop = 0;
+
+ this->generateAdvance(glyph);
+
+ RECT bbox;
+ HRVM(this->getBoundingBox(glyph, fRenderingMode, fTextureType, &bbox),
+ "Requested bounding box could not be determined.");
+
+ if (glyph_check_and_set_bounds(glyph, bbox)) {
+ return;
+ }
+
+ // GetAlphaTextureBounds succeeds but returns an empty RECT if there are no
+ // glyphs of the specified texture type. When this happens, try with the
+ // alternate texture type.
+ if (DWRITE_TEXTURE_CLEARTYPE_3x1 == fTextureType) {
+ HRVM(this->getBoundingBox(glyph,
+ DWRITE_RENDERING_MODE_ALIASED,
+ DWRITE_TEXTURE_ALIASED_1x1,
+ &bbox),
+ "Fallback bounding box could not be determined.");
+ if (glyph_check_and_set_bounds(glyph, bbox)) {
+ glyph->fForceBW = 1;
+ }
+ }
+ // TODO: handle the case where a request for DWRITE_TEXTURE_ALIASED_1x1
+ // fails, and try DWRITE_TEXTURE_CLEARTYPE_3x1.
}
void SkScalerContext_DW::generateFontMetrics(SkPaint::FontMetrics* metrics) {
}
}
-const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph) {
+const void* SkScalerContext_DW::drawDWMask(const SkGlyph& glyph,
+ DWRITE_RENDERING_MODE renderingMode,
+ DWRITE_TEXTURE_TYPE textureType)
+{
int sizeNeeded = glyph.fWidth * glyph.fHeight;
- if (DWRITE_RENDERING_MODE_ALIASED != fRenderingMode) {
+ if (DWRITE_RENDERING_MODE_ALIASED != renderingMode) {
sizeNeeded *= 3;
}
if (sizeNeeded > fBits.count()) {
HRNM(fTypeface->fFactory->CreateGlyphRunAnalysis(&run,
1.0f, // pixelsPerDip,
&fXform,
- fRenderingMode,
+ renderingMode,
fMeasuringMode,
0.0f, // baselineOriginX,
0.0f, // baselineOriginY,
bbox.top = glyph.fTop;
bbox.right = glyph.fLeft + glyph.fWidth;
bbox.bottom = glyph.fTop + glyph.fHeight;
- HRNM(glyphRunAnalysis->CreateAlphaTexture(fTextureType,
+ HRNM(glyphRunAnalysis->CreateAlphaTexture(textureType,
&bbox,
fBits.begin(),
sizeNeeded),
void SkScalerContext_DW::generateImage(const SkGlyph& glyph) {
//Create the mask.
- const void* bits = this->drawDWMask(glyph);
+ DWRITE_RENDERING_MODE renderingMode = fRenderingMode;
+ DWRITE_TEXTURE_TYPE textureType = fTextureType;
+ if (glyph.fForceBW) {
+ renderingMode = DWRITE_RENDERING_MODE_ALIASED;
+ textureType = DWRITE_TEXTURE_ALIASED_1x1;
+ }
+ const void* bits = this->drawDWMask(glyph, renderingMode, textureType);
if (!bits) {
sk_bzero(glyph.fImage, glyph.computeImageSize());
return;
//Copy the mask into the glyph.
const uint8_t* src = (const uint8_t*)bits;
- if (DWRITE_RENDERING_MODE_ALIASED == fRenderingMode) {
+ if (DWRITE_RENDERING_MODE_ALIASED == renderingMode) {
bilevel_to_bw(src, glyph);
const_cast<SkGlyph&>(glyph).fMaskFormat = SkMask::kBW_Format;
} else if (!isLCD(fRec)) {
virtual void generateFontMetrics(SkPaint::FontMetrics*) SK_OVERRIDE;
private:
- const void* drawDWMask(const SkGlyph& glyph);
+ const void* drawDWMask(const SkGlyph& glyph,
+ DWRITE_RENDERING_MODE renderingMode,
+ DWRITE_TEXTURE_TYPE textureType);
+
+ HRESULT getBoundingBox(SkGlyph* glyph,
+ DWRITE_RENDERING_MODE renderingMode,
+ DWRITE_TEXTURE_TYPE textureType,
+ RECT* bbox);
SkTDArray<uint8_t> fBits;
/** The total matrix without the text height scale. */
}
}
+static void drawSaveLayerPicture(int width, int height, int tileSize, SkBBHFactory* factory, SkBitmap* result) {
+
+ SkMatrix matrix;
+ matrix.setTranslate(SkIntToScalar(50), 0);
+
+ SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorWHITE, SkXfermode::kSrc_Mode));
+ SkAutoTUnref<SkImageFilter> cfif(SkColorFilterImageFilter::Create(cf.get()));
+ SkAutoTUnref<SkImageFilter> imageFilter(SkMatrixImageFilter::Create(matrix, SkPaint::kNone_FilterLevel, cfif.get()));
+
+ SkPaint paint;
+ paint.setImageFilter(imageFilter.get());
+ SkPictureRecorder recorder;
+ SkRect bounds = SkRect::Make(SkIRect::MakeXYWH(0, 0, 50, 50));
+ SkCanvas* recordingCanvas = recorder.beginRecording(width, height, factory, 0);
+ recordingCanvas->translate(-55, 0);
+ recordingCanvas->saveLayer(&bounds, &paint);
+ recordingCanvas->restore();
+ SkAutoTUnref<SkPicture> picture1(recorder.endRecording());
+
+ result->allocN32Pixels(width, height);
+ SkCanvas canvas(*result);
+ canvas.clear(0);
+ canvas.clipRect(SkRect::Make(SkIRect::MakeWH(tileSize, tileSize)));
+ canvas.drawPicture(picture1.get());
+}
+
+DEF_TEST(ImageFilterDrawMatrixBBH, reporter) {
+ // Check that matrix filter when drawn tiled with BBH exactly
+ // matches the same thing drawn without BBH.
+ // Tests pass by not asserting.
+
+ const int width = 200, height = 200;
+ const int tileSize = 100;
+ SkBitmap result1, result2;
+ SkRTreeFactory factory;
+
+ drawSaveLayerPicture(width, height, tileSize, &factory, &result1);
+ drawSaveLayerPicture(width, height, tileSize, NULL, &result2);
+
+ for (int y = 0; y < height; y++) {
+ int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
+ REPORTER_ASSERT(reporter, !diffs);
+ if (diffs) {
+ break;
+ }
+ }
+}
+
static void drawBlurredRect(SkCanvas* canvas) {
SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(SkIntToScalar(8), 0));
SkPaint filterPaint;
testPathFailOp(reporter, path, pathB, kUnion_PathOp, filename);
}
+// m 100,0 60,170 -160,-110 200,0 -170,11000000000 z
+static void fuzz433(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path1, path2;
+ path1.moveTo(100,0);
+ path1.lineTo(60,170);
+ path1.lineTo(-160,-110);
+ path1.lineTo(200,0);
+ path1.lineTo(-170,11000000000.0f);
+ path1.close();
+
+ path2.moveTo(100 + 20,0 + 20);
+ path2.lineTo(60 + 20,170 + 20);
+ path2.lineTo(-160 + 20,-110 + 20);
+ path2.lineTo(200 + 20,0 + 20);
+ path2.lineTo(-170 + 20,11000000000.0f + 20);
+ path2.close();
+
+ testPathFailOp(reporter, path1, path2, kIntersect_PathOp, filename);
+}
+
+static void fuzz433b(skiatest::Reporter* reporter, const char* filename) {
+ SkPath path1, path2;
+ path1.setFillType(SkPath::kEvenOdd_FillType);
+ path1.moveTo(140, 40);
+ path1.lineTo(200, 210);
+ path1.lineTo(40, 100);
+ path1.lineTo(240, 100);
+ path1.lineTo(70, 1.1e+10f);
+ path1.lineTo(140, 40);
+ path1.close();
+
+ path1.setFillType(SkPath::kWinding_FillType);
+ path2.moveTo(190, 60);
+ path2.lineTo(250, 230);
+ path2.lineTo(90, 120);
+ path2.lineTo(290, 120);
+ path2.lineTo(120, 1.1e+10f);
+ path2.lineTo(190, 60);
+ path2.close();
+
+ testPathFailOp(reporter, path1, path2, kUnion_PathOp, filename);
+}
+
static struct TestDesc failTests[] = {
+ TEST(fuzz433b),
+ TEST(fuzz433),
TEST(bufferOverflow),
};
canvas->drawPath(path, paint);
}
+/**
+ * In debug mode, this path was causing an assertion to fail in
+ * SkPathStroker::preJoinTo() and, in Release, the use of an unitialized value.
+ */
+static void make_path_crbugskia2820(SkPath* path, skiatest::Reporter* reporter) {
+ SkPoint orig, p1, p2, p3;
+ orig = SkPoint::Make(1.f, 1.f);
+ p1 = SkPoint::Make(1.f - SK_ScalarNearlyZero, 1.f);
+ p2 = SkPoint::Make(1.f, 1.f + SK_ScalarNearlyZero);
+ p3 = SkPoint::Make(2.f, 2.f);
+
+ path->reset();
+ path->moveTo(orig);
+ path->cubicTo(p1, p2, p3);
+ path->close();
+}
+
+static void test_path_crbugskia2820(skiatest::Reporter* reporter) {//GrContext* context) {
+ SkPath path;
+ make_path_crbugskia2820(&path, reporter);
+
+ SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
+ stroke.setStrokeStyle(2 * SK_Scalar1);
+ stroke.applyToPath(&path, path);
+}
+
static void make_path0(SkPath* path) {
// from * https://code.google.com/p/skia/issues/detail?id=1706
PathTest_Private::TestPathTo(reporter);
PathRefTest_Private::TestPathRef(reporter);
test_dump(reporter);
+ test_path_crbugskia2820(reporter);
}
</summary>
</histogram>
+<histogram name="Media.AudioRendererAudioGlitches" enum="AudioGlitchResult">
+ <owner>henrika@chromium.org</owner>
+ <summary>
+ Captures if render-side audio glitches are detected or not. Sampled when a
+ low-latency output audio stream is destructed.
+ </summary>
+</histogram>
+
<histogram name="Media.AudioRendererEvents" enum="AudioRendererEvents">
<owner>Please list the metric's owners. Add more owner tags as needed.</owner>
<summary>Captures statistics for various AudioRendererImpl events.</summary>
<int value="8" label="k1920"/>
</enum>
+<enum name="AudioGlitchResult" type="int">
+ <int value="0" label="No audio glitches"/>
+ <int value="1" label="Audio glitches"/>
+</enum>
+
<enum name="AudioInputSilenceReport" type="int">
<int value="0" label="No measurement"/>
<int value="1" label="Only audio"/>
<int value="880" label="EASYUNLOCKPRIVATE_CLEARPERMITACCESS"/>
<int value="881" label="EASYUNLOCKPRIVATE_SETREMOTEDEVICES"/>
<int value="882" label="EASYUNLOCKPRIVATE_GETREMOTEDEVICES"/>
+ <int value="883" label="FILESYSTEMPROVIDER_GETALL"/>
+ <int value="884"
+ label="EASYUNLOCKPRIVATE_CONNECTTOBLUETOOTHSERVICEINSECURELY"/>
</enum>
<enum name="ExtensionInstallCause" type="int">
<int value="1" label="Add new user from title bar menu"/>
<int value="2" label="Add new user from settings dialog"/>
<int value="3" label="Add new user from the User Manager"/>
+ <int value="4" label="Auto-created after deleting last user"/>
</enum>
<enum name="ProfileAndroidAccountManagementMenu" type="int">
<int value="8" label="SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST"/>
<int value="9" label="SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED"/>
<int value="10" label="SERVICE_UTILITY_SEMANTIC_CAPS_FAILED"/>
+ <int value="11" label="SERVICE_UTILITY_FAILED_TO_START"/>
</enum>
<enum name="ServiceWorkerDatabaseStatus" type="int">
<suffix name="SSL" label="Bypass due to SSL"/>
<suffix name="LocalBypassRules"
label="Bypass due to client-side bypass rules"/>
+ <suffix name="ManagedProxyConfig" label="Bypass due to a managed config"/>
<suffix name="Current" label="Bypass due to explicit instruction"/>
<suffix name="ShortAll" label="Short bypass"/>
<suffix name="ShortTriggeringRequest"
private static final long NANOSECONDS_PER_MILLISECOND = 1000000;
private static final long NANOSECONDS_PER_MICROSECOND = 1000;
+ private boolean mInsideVSync = false;
+
/**
* VSync listener class
*/
mGoodStartingPointNano = goodStartingPointNano;
}
+ /**
+ * @return true if onVSync handler is executing. If onVSync handler
+ * introduces invalidations, View#invalidate() should be called. If
+ * View#postInvalidateOnAnimation is called instead, the corresponding onDraw
+ * will be delayed by one frame. The embedder of VSyncMonitor should check
+ * this value if it wants to post an invalidation.
+ */
+ public boolean isInsideVSync() {
+ return mInsideVSync;
+ }
+
private long getCurrentNanoTime() {
return System.nanoTime();
}
private void onVSyncCallback(long frameTimeNanos, long currentTimeNanos) {
assert mHaveRequestInFlight;
+ mInsideVSync = true;
mHaveRequestInFlight = false;
mLastVSyncCpuTimeNano = currentTimeNanos;
- if (mListener != null) {
- mListener.onVSync(this, frameTimeNanos / NANOSECONDS_PER_MICROSECOND);
+ try {
+ if (mListener != null) {
+ mListener.onVSync(this, frameTimeNanos / NANOSECONDS_PER_MICROSECOND);
+ }
+ } finally {
+ mInsideVSync = false;
}
}
import android.app.Activity;
import android.content.ClipData;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
-import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;
+import android.util.Log;
import org.chromium.base.CalledByNative;
import org.chromium.base.ContentUriUtils;
import org.chromium.ui.R;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
*/
@JNINamespace("ui")
class SelectFileDialog implements WindowAndroid.IntentCallback{
+ private static final String TAG = "SelectFileDialog";
private static final String IMAGE_TYPE = "image/";
private static final String VIDEO_TYPE = "video/";
private static final String AUDIO_TYPE = "audio/";
private static final String ALL_AUDIO_TYPES = AUDIO_TYPE + "*";
private static final String ANY_TYPES = "*/*";
private static final String CAPTURE_IMAGE_DIRECTORY = "browser-photos";
+ // Keep this variable in sync with the value defined in file_paths.xml.
+ private static final String IMAGE_FILE_PATH = "images";
private final long mNativeSelectFileDialog;
private List<String> mFileTypes;
Intent chooser = new Intent(Intent.ACTION_CHOOSER);
Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
- mCameraOutputUri = Uri.fromFile(getFileForImageCapture());
+ camera.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ Context context = window.getApplicationContext();
+ try {
+ mCameraOutputUri = ContentUriUtils.getContentUriFromFile(
+ context, getFileForImageCapture(context));
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot retrieve content uri from file", e);
+ }
+ if (mCameraOutputUri == null) {
+ onFileNotSelected();
+ return;
+ }
+
camera.putExtra(MediaStore.EXTRA_OUTPUT, mCameraOutputUri);
+ camera.setClipData(
+ ClipData.newUri(context.getContentResolver(), IMAGE_FILE_PATH, mCameraOutputUri));
Intent camcorder = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
Intent soundRecorder = new Intent(
MediaStore.Audio.Media.RECORD_SOUND_ACTION);
}
/**
- * Get a file for the image capture in the CAPTURE_IMAGE_DIRECTORY directory.
+ * Get a file for the image capture in the IMAGE_FILE_PATH directory.
+ * @param context The application context.
*/
- private File getFileForImageCapture() {
- File externalDataDir = Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_DCIM);
- File cameraDataDir = new File(externalDataDir.getAbsolutePath() +
- File.separator + CAPTURE_IMAGE_DIRECTORY);
- if (!cameraDataDir.exists() && !cameraDataDir.mkdirs()) {
- cameraDataDir = externalDataDir;
+ private File getFileForImageCapture(Context context) throws IOException {
+ final File path = new File(context.getFilesDir(), IMAGE_FILE_PATH);
+ if (!path.exists() && !path.mkdir()) {
+ throw new IOException("Folder cannot be created.");
}
- File photoFile = new File(cameraDataDir.getAbsolutePath() +
- File.separator + System.currentTimeMillis() + ".jpg");
+ File photoFile = File.createTempFile(
+ String.valueOf(System.currentTimeMillis()), ".jpg", path);
return photoFile;
}
if (results == null) {
// If we have a successful return but no data, then assume this is the camera returning
// the photo that we requested.
- nativeOnFileSelected(mNativeSelectFileDialog, mCameraOutputUri.getPath(), "");
+ nativeOnFileSelected(mNativeSelectFileDialog, mCameraOutputUri.toString(),
+ mCameraOutputUri.getLastPathSegment());
// Broadcast to the media scanner that there's a new photo on the device so it will
// show up right away in the gallery (rather than waiting until the next time the media
};
/**
+ * @return true if onVSync handler is executing.
+ * @see org.chromium.ui.VSyncMonitor#isInsideVSync().
+ */
+ public boolean isInsideVSync() {
+ return mVSyncMonitor.isInsideVSync();
+ }
+
+ /**
* @param context The application context.
*/
@SuppressLint("UseSparseArrays")
return base::i18n::GetCanonicalLocale(locale.c_str());
}
+std::string GetLanguage(const std::string& locale) {
+ const std::string::size_type hyphen_pos = locale.find('-');
+ return std::string(locale, 0, hyphen_pos);
+}
+
bool CheckAndResolveLocale(const std::string& locale,
std::string* resolved_locale) {
#if defined(OS_MACOSX)
#endif
}
-std::string GetApplicationLocale(const std::string& pref_locale) {
+std::string GetApplicationLocaleInternal(const std::string& pref_locale) {
#if defined(OS_MACOSX)
// Use any override (Cocoa for the browser), otherwise use the preference
if (app_locale.empty())
app_locale = "en-US";
- // Windows/Linux call SetICUDefaultLocale after determining the actual locale
- // with CheckAndResolveLocal to make ICU APIs work in that locale.
- // Mac doesn't use a locale directory tree of resources (it uses Mac style
- // resources), so mirror the Windows/Linux behavior of calling
- // SetICUDefaultLocale.
- base::i18n::SetICUDefaultLocale(app_locale);
return app_locale;
#else
std::vector<std::string>::const_iterator i = candidates.begin();
for (; i != candidates.end(); ++i) {
if (CheckAndResolveLocale(*i, &resolved_locale)) {
- base::i18n::SetICUDefaultLocale(resolved_locale);
return resolved_locale;
}
}
// Fallback on en-US.
const std::string fallback_locale("en-US");
if (IsLocaleAvailable(fallback_locale)) {
- base::i18n::SetICUDefaultLocale(fallback_locale);
return fallback_locale;
}
#endif
}
+std::string GetApplicationLocale(const std::string& pref_locale,
+ bool set_icu_locale) {
+ const std::string locale = GetApplicationLocaleInternal(pref_locale);
+ if (set_icu_locale && !locale.empty())
+ base::i18n::SetICUDefaultLocale(locale);
+ return locale;
+}
+
+std::string GetApplicationLocale(const std::string& pref_locale) {
+ return GetApplicationLocale(pref_locale, true /* set_icu_locale */);
+}
+
bool IsLocaleNameTranslated(const char* locale,
const std::string& display_locale) {
base::string16 display_name =
// std::string as an argument.
UI_BASE_EXPORT std::string GetCanonicalLocale(const std::string& locale);
+// Takes normalized locale as |locale|. Returns language part (before '-').
+UI_BASE_EXPORT std::string GetLanguage(const std::string& locale);
+
// This method translates a generic locale name to one of the locally defined
// ones. This method returns true if it succeeds.
UI_BASE_EXPORT bool CheckAndResolveLocale(const std::string& locale,
// command line (--lang), second we try the value in the prefs file (passed in
// as |pref_locale|), finally, we fall back on the system locale. We only return
// a value if there's a corresponding resource DLL for the locale. Otherwise,
-// we fall back to en-us.
+// we fall back to en-us. |set_icu_locale| determines whether the resulting
+// locale is set as the default ICU locale before returning it.
+UI_BASE_EXPORT std::string GetApplicationLocale(const std::string& pref_locale,
+ bool set_icu_locale);
+
+// Convenience version of GetApplicationLocale() that sets the resulting locale
+// as the default ICU locale before returning it.
UI_BASE_EXPORT std::string GetApplicationLocale(const std::string& pref_locale);
// Returns true if a display name for |locale| is available in the locale
base::i18n::SetICUDefaultLocale("en-US");
env->SetVar("LANGUAGE", "xx:fr_CA");
EXPECT_EQ("fr", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("fr", icu::Locale::getDefault().getLanguage());
env->SetVar("LANGUAGE", "xx:yy:en_gb.utf-8@quot");
EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
env->SetVar("LANGUAGE", "xx:zh-hk");
EXPECT_EQ("zh-TW", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("zh", icu::Locale::getDefault().getLanguage());
// We emulate gettext's behavior here, which ignores LANG/LC_MESSAGES/LC_ALL
// when LANGUAGE is specified. If no language specified in LANGUAGE is
base::i18n::SetICUDefaultLocale("fr-FR");
env->SetVar("LANGUAGE", "xx:yy");
EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
env->SetVar("LANGUAGE", "/fr:zh_CN");
EXPECT_EQ("zh-CN", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("zh", icu::Locale::getDefault().getLanguage());
// Test prioritization of the different environment variables.
env->SetVar("LANGUAGE", "fr");
env->SetVar("LC_MESSAGES", "he");
env->SetVar("LANG", "nb");
EXPECT_EQ("fr", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("fr", icu::Locale::getDefault().getLanguage());
env->UnSetVar("LANGUAGE");
EXPECT_EQ("es", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("es", icu::Locale::getDefault().getLanguage());
env->UnSetVar("LC_ALL");
EXPECT_EQ("he", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("he", icu::Locale::getDefault().getLanguage());
env->UnSetVar("LC_MESSAGES");
EXPECT_EQ("nb", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("nb", icu::Locale::getDefault().getLanguage());
env->UnSetVar("LANG");
SetDefaultLocaleForTest("ca", env.get());
EXPECT_EQ("ca", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("ca", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("ca-ES", env.get());
EXPECT_EQ("ca", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("ca", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("ca@valencia", env.get());
EXPECT_EQ("ca@valencia", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("ca", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("ca_ES@valencia", env.get());
EXPECT_EQ("ca@valencia", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("ca", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("ca_ES.UTF8@valencia", env.get());
EXPECT_EQ("ca@valencia", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("ca", icu::Locale::getDefault().getLanguage());
}
SetDefaultLocaleForTest("en-US", env.get());
EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("xx", env.get());
EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
if (!kPlatformHasDefaultLocale) {
// ChromeOS & embedded use only browser prefs in GetApplicationLocale(),
// the default locale from the OS or environment.
SetDefaultLocaleForTest("en-GB", env.get());
EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(""));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("en-US", env.get());
EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale("en-GB"));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("en-US", env.get());
EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale("en-AU"));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("en-US", env.get());
EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale("en-NZ"));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("en-US", env.get());
EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale("en-CA"));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("en-US", env.get());
EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale("en-ZA"));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
} else {
// Most platforms have an OS-provided locale. This locale is preferred.
SetDefaultLocaleForTest("en-GB", env.get());
EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("fr-CA", env.get());
EXPECT_EQ("fr", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("fr", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("es-MX", env.get());
EXPECT_EQ("es-419", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("es", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("es-AR", env.get());
EXPECT_EQ("es-419", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("es", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("es-ES", env.get());
EXPECT_EQ("es", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("es", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("es", env.get());
EXPECT_EQ("es", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("es", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("zh-HK", env.get());
EXPECT_EQ("zh-TW", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("zh", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("zh-MO", env.get());
EXPECT_EQ("zh-TW", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("zh", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("zh-SG", env.get());
EXPECT_EQ("zh-CN", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("zh", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("en-CA", env.get());
EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("en-AU", env.get());
EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("en-NZ", env.get());
EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
SetDefaultLocaleForTest("en-ZA", env.get());
EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale(std::string()));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
}
+ SetDefaultLocaleForTest("en-US", env.get());
+
if (kSupportsLocalePreference) {
// On windows, the user can override the locale in preferences.
base::i18n::SetICUDefaultLocale("en-US");
EXPECT_EQ("fr", l10n_util::GetApplicationLocale("fr"));
+ EXPECT_STREQ("fr", icu::Locale::getDefault().getLanguage());
EXPECT_EQ("fr", l10n_util::GetApplicationLocale("fr-CA"));
+ EXPECT_STREQ("fr", icu::Locale::getDefault().getLanguage());
base::i18n::SetICUDefaultLocale("en-US");
// Aliases iw, no, tl to he, nb, fil.
EXPECT_EQ("he", l10n_util::GetApplicationLocale("iw"));
+ EXPECT_STREQ("he", icu::Locale::getDefault().getLanguage());
EXPECT_EQ("nb", l10n_util::GetApplicationLocale("no"));
+ EXPECT_STREQ("nb", icu::Locale::getDefault().getLanguage());
EXPECT_EQ("fil", l10n_util::GetApplicationLocale("tl"));
+ EXPECT_STREQ("fil", icu::Locale::getDefault().getLanguage());
// es-419 and es-XX (where XX is not Spain) should be
// mapped to es-419 (Latin American Spanish).
EXPECT_EQ("es-419", l10n_util::GetApplicationLocale("es-419"));
+ EXPECT_STREQ("es", icu::Locale::getDefault().getLanguage());
EXPECT_EQ("es", l10n_util::GetApplicationLocale("es-ES"));
+ EXPECT_STREQ("es", icu::Locale::getDefault().getLanguage());
EXPECT_EQ("es-419", l10n_util::GetApplicationLocale("es-AR"));
+ EXPECT_STREQ("es", icu::Locale::getDefault().getLanguage());
base::i18n::SetICUDefaultLocale("es-AR");
EXPECT_EQ("es", l10n_util::GetApplicationLocale("es"));
+ EXPECT_STREQ("es", icu::Locale::getDefault().getLanguage());
base::i18n::SetICUDefaultLocale("zh-HK");
EXPECT_EQ("zh-CN", l10n_util::GetApplicationLocale("zh-CN"));
+ EXPECT_STREQ("zh", icu::Locale::getDefault().getLanguage());
base::i18n::SetICUDefaultLocale("he");
EXPECT_EQ("en-US", l10n_util::GetApplicationLocale("en"));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
+
+ base::i18n::SetICUDefaultLocale("he");
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale("en", false));
+ EXPECT_STREQ("he", icu::Locale::getDefault().getLanguage());
+
+ base::i18n::SetICUDefaultLocale("de");
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale("xx", false));
+ EXPECT_STREQ("de", icu::Locale::getDefault().getLanguage());
+
+ base::i18n::SetICUDefaultLocale("de");
+ EXPECT_EQ("fr", l10n_util::GetApplicationLocale("fr", false));
+ EXPECT_STREQ("de", icu::Locale::getDefault().getLanguage());
+
+ base::i18n::SetICUDefaultLocale("de");
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale("en", false));
+ EXPECT_STREQ("de", icu::Locale::getDefault().getLanguage());
+
+ base::i18n::SetICUDefaultLocale("de");
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale("en-US", true));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
+ } else {
+ base::i18n::SetICUDefaultLocale("de");
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(std::string(), false));
+ EXPECT_STREQ("de", icu::Locale::getDefault().getLanguage());
+
+ base::i18n::SetICUDefaultLocale("de");
+ EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(std::string(), true));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
}
#if defined(OS_WIN)
if (base::win::GetVersion() < base::win::VERSION_VISTA) {
base::i18n::SetICUDefaultLocale("am");
EXPECT_EQ("en-US", l10n_util::GetApplicationLocale(""));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
base::i18n::SetICUDefaultLocale("en-GB");
EXPECT_EQ("en-GB", l10n_util::GetApplicationLocale("am"));
+ EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
} else {
base::i18n::SetICUDefaultLocale("am");
EXPECT_EQ("am", l10n_util::GetApplicationLocale(""));
+ EXPECT_STREQ("am", icu::Locale::getDefault().getLanguage());
base::i18n::SetICUDefaultLocale("en-GB");
EXPECT_EQ("am", l10n_util::GetApplicationLocale("am"));
+ EXPECT_STREQ("am", icu::Locale::getDefault().getLanguage());
}
#endif // defined(OS_WIN)
}
if (alpha_shape_) {
filters.Append(cc::FilterOperation::CreateAlphaThresholdFilter(
- *alpha_shape_, 1.f, 0.f));
+ *alpha_shape_, 0.f, 0.f));
}
cc_layer_->SetFilters(filters);
WaitForDraw();
}
+void ExpectRgba(int x, int y, SkColor expected_color, SkColor actual_color) {
+ EXPECT_EQ(expected_color, actual_color)
+ << "Pixel error at x=" << x << " y=" << y << "; "
+ << "actual RGBA=("
+ << SkColorGetR(actual_color) << ","
+ << SkColorGetG(actual_color) << ","
+ << SkColorGetB(actual_color) << ","
+ << SkColorGetA(actual_color) << "); "
+ << "expected RGBA=("
+ << SkColorGetR(expected_color) << ","
+ << SkColorGetG(expected_color) << ","
+ << SkColorGetB(expected_color) << ","
+ << SkColorGetA(expected_color) << ")";
+}
+
// Checks that pixels are actually drawn to the screen with a read back.
TEST_F(LayerWithRealCompositorTest, DrawPixels) {
gfx::Size viewport_size = GetCompositor()->size();
for (int y = 0; y < viewport_size.height(); y++) {
SkColor actual_color = bitmap.getColor(x, y);
SkColor expected_color = y < blue_height ? SK_ColorBLUE : SK_ColorRED;
- EXPECT_EQ(expected_color, actual_color)
- << "Pixel error at x=" << x << " y=" << y << "; "
- << "actual RGBA=("
- << SkColorGetR(actual_color) << ","
- << SkColorGetG(actual_color) << ","
- << SkColorGetB(actual_color) << ","
- << SkColorGetA(actual_color) << "); "
- << "expected RGBA=("
- << SkColorGetR(expected_color) << ","
- << SkColorGetG(expected_color) << ","
- << SkColorGetB(expected_color) << ","
- << SkColorGetA(expected_color) << ")";
+ ExpectRgba(x, y, expected_color, actual_color);
+ }
+ }
+}
+
+// Checks that drawing a layer with transparent pixels is blended correctly
+// with the lower layer.
+TEST_F(LayerWithRealCompositorTest, DrawAlphaBlendedPixels) {
+ gfx::Size viewport_size = GetCompositor()->size();
+
+ int test_size = 200;
+ EXPECT_GE(viewport_size.width(), test_size);
+ EXPECT_GE(viewport_size.height(), test_size);
+
+ // Blue with a wee bit of transparency.
+ SkColor blue_with_alpha = SkColorSetARGBInline(40, 10, 20, 200);
+ SkColor blend_color = SkColorSetARGBInline(255, 216, 3, 32);
+
+ scoped_ptr<Layer> background_layer(
+ CreateColorLayer(SK_ColorRED, gfx::Rect(viewport_size)));
+ scoped_ptr<Layer> foreground_layer(
+ CreateColorLayer(blue_with_alpha, gfx::Rect(viewport_size)));
+
+ // This must be set to false for layers with alpha to be blended correctly.
+ foreground_layer->SetFillsBoundsOpaquely(false);
+
+ background_layer->Add(foreground_layer.get());
+ DrawTree(background_layer.get());
+
+ SkBitmap bitmap;
+ ASSERT_TRUE(ReadPixels(&bitmap, gfx::Rect(viewport_size)));
+ ASSERT_FALSE(bitmap.empty());
+
+ SkAutoLockPixels lock(bitmap);
+ for (int x = 0; x < test_size; x++) {
+ for (int y = 0; y < test_size; y++) {
+ SkColor actual_color = bitmap.getColor(x, y);
+ ExpectRgba(x, y, blend_color, actual_color);
+ }
+ }
+}
+
+// Checks that using the AlphaShape filter applied to a layer with
+// transparency, alpha-blends properly with the layer below.
+TEST_F(LayerWithRealCompositorTest, DrawAlphaThresholdFilterPixels) {
+ gfx::Size viewport_size = GetCompositor()->size();
+
+ int test_size = 200;
+ EXPECT_GE(viewport_size.width(), test_size);
+ EXPECT_GE(viewport_size.height(), test_size);
+
+ int blue_height = 10;
+ SkColor blue_with_alpha = SkColorSetARGBInline(40, 0, 0, 255);
+ SkColor blend_color = SkColorSetARGBInline(255, 215, 0, 40);
+
+ scoped_ptr<Layer> background_layer(
+ CreateColorLayer(SK_ColorRED, gfx::Rect(viewport_size)));
+ scoped_ptr<Layer> foreground_layer(
+ CreateColorLayer(blue_with_alpha, gfx::Rect(viewport_size)));
+
+ // Add a shape to restrict the visible part of the layer.
+ SkRegion shape;
+ shape.setRect(0, 0, viewport_size.width(), blue_height);
+ foreground_layer->SetAlphaShape(make_scoped_ptr(new SkRegion(shape)));
+
+ foreground_layer->SetFillsBoundsOpaquely(false);
+
+ background_layer->Add(foreground_layer.get());
+ DrawTree(background_layer.get());
+
+ SkBitmap bitmap;
+ ASSERT_TRUE(ReadPixels(&bitmap, gfx::Rect(viewport_size)));
+ ASSERT_FALSE(bitmap.empty());
+
+ SkAutoLockPixels lock(bitmap);
+ for (int x = 0; x < test_size; x++) {
+ for (int y = 0; y < test_size; y++) {
+ SkColor actual_color = bitmap.getColor(x, y);
+ ExpectRgba(x, y, actual_color,
+ y < blue_height ? blend_color : SK_ColorRED);
}
}
}
color: #3d3d3d;
cursor: default;
flex: 1 1 auto;
- font-family: Open Sans, Droid Sans Fallback, sans-serif;
+ font-family: Noto Sans UI, sans-serif;
font-size: 10pt;
position: relative;
}
cursor: default;
display: flex;
flex-direction: column;
- font-family: Open Sans, Droid Sans Fallback, sans-serif;
+ font-family: Noto Sans UI, sans-serif;
font-size: 10pt;
justify-content: flex-start;
left: 0;
line-height: 45px;
}
-.dialog-navigation-list-header #profile-badge {
- display: inline-block;
- height: 29px; /* Size of a profile image. */
- margin: 8px;
- vertical-align: top;
- width: 29px; /* Size of a profile image. */
-}
-
.dialog-navigation-list-header #app-name {
color: #303030;
font-size: 130%;
margin: 0 15px;
}
-.dialog-navigation-list-header #profile-badge:not([hidden]) + #app-name {
- margin: 0;
-}
-
.dialog-navigation-list-contents {
display: flex;
flex: 1 1 auto;
this.onShowGearMenu_.bind(this));
chrome.fileBrowserPrivate.onDesktopChanged.addListener(function() {
this.updateVisitDesktopMenus_();
- this.ui_.updateProfileBadge();
}.bind(this));
chrome.fileBrowserPrivate.onProfileAdded.addListener(
this.updateVisitDesktopMenus_.bind(this));
Object.seal(this);
// Initialize the header.
- this.updateProfileBadge();
this.element_.querySelector('#app-name').innerText =
chrome.runtime.getManifest().name;
};
/**
- * Updates visibility and image of the profile badge.
- */
-FileManagerUI.prototype.updateProfileBadge = function() {
- if (this.dialogType_ !== DialogType.FULL_PAGE)
- return;
-
- chrome.fileBrowserPrivate.getProfiles(function(profiles,
- currentId,
- displayedId) {
- var profileImage;
- if (currentId !== displayedId) {
- for (var i = 0; i < profiles.length; i++) {
- if (profiles[i].profileId === currentId) {
- profileImage = profiles[i].profileImage;
- break;
- }
- }
- }
- var profileBadge = this.element_.querySelector('#profile-badge');
- if (profileImage) {
- profileBadge.style.background =
- '-webkit-image-set(' +
- 'url(' + profileImage.scale1xUrl + ') 1x,' +
- 'url(' + profileImage.scale2xUrl + ') 2x) no-repeat center';
- profileBadge.hidden = false;
- } else {
- profileBadge.style.background = '';
- profileBadge.hidden = true;
- }
- }.bind(this));
-};
-
-/**
* Handles click event on the search button.
* @param {Event} event Click event.
* @private
<div class="dialog-container">
<div class="dialog-navigation-list">
<div class="dialog-navigation-list-header">
- <span id="profile-badge" hidden></span><span id="app-name"></span>
+ <span id="app-name"></span>
</div>
<div class="dialog-navigation-list-contents">
<tree id="directory-tree" tabindex="15"></tree>
body {
-webkit-user-select: none;
- font-family: Open Sans, Droid Sans Fallback, sans-serif;
+ font-family: Noto Sans UI, sans-serif;
font-size: 84%;
margin: 0;
}
}
} else {
document.title = '';
+ this.filenameEdit_.disabled = true;
this.filenameEdit_.value = '';
this.shareButton_.hidden = true;
}
body {
-webkit-user-select: none;
background: black;
- font-family: Noto Sans UI,Droid Sans Fallback,sans-serif;
+ font-family: Noto Sans UI,sans-serif;
font-size: 84%;
margin: 0;
overflow: hidden;
return Derive(0, font_style);
}
+gfx::FontList FontList::DeriveWithHeightUpperBound(int height) const {
+ gfx::FontList font_list(*this);
+ for (int font_size = font_list.GetFontSize(); font_size > 1; --font_size) {
+ const int internal_leading =
+ font_list.GetBaseline() - font_list.GetCapHeight();
+ // Some platforms don't support getting the cap height, and simply return
+ // the entire font ascent from GetCapHeight(). Centering the ascent makes
+ // the font look too low, so if GetCapHeight() returns the ascent, center
+ // the entire font height instead.
+ const int space =
+ height - ((internal_leading != 0) ?
+ font_list.GetCapHeight() : font_list.GetHeight());
+ const int y_offset = space / 2 - internal_leading;
+ const int space_at_bottom = height - (y_offset + font_list.GetHeight());
+ if ((y_offset >= 0) && (space_at_bottom >= 0))
+ break;
+ font_list = font_list.DeriveWithSizeDelta(-1);
+ }
+ return font_list;
+}
+
int FontList::GetHeight() const {
return impl_->GetHeight();
}
// values: Font::BOLD, Font::ITALIC and Font::UNDERLINE.
FontList DeriveWithStyle(int font_style) const;
+ // Shrinks the font size until the font list fits within |height| while
+ // having its cap height vertically centered. Returns a new FontList with
+ // the correct height.
+ //
+ // The expected layout:
+ // +--------+-----------------------------------------------+------------+
+ // | | y offset | space |
+ // | +--------+-------------------+------------------+ above |
+ // | | | | internal leading | cap height |
+ // | box | font | ascent (baseline) +------------------+------------+
+ // | height | height | | cap height |
+ // | | |-------------------+------------------+------------+
+ // | | | descent (height - baseline) | space |
+ // | +--------+--------------------------------------+ below |
+ // | | space at bottom | cap height |
+ // +--------+-----------------------------------------------+------------+
+ // Goal:
+ // center of box height == center of cap height
+ // (i.e. space above cap height == space below cap height)
+ // Restrictions:
+ // y offset >= 0
+ // space at bottom >= 0
+ // (i.e. Entire font must be visible inside the box.)
+ gfx::FontList DeriveWithHeightUpperBound(int height) const;
+
// Returns the height of this font list, which is max(ascent) + max(descent)
// for all the fonts in the font list.
int GetHeight() const;
font_list_mix.GetBaseline());
}
+TEST(FontListTest, Fonts_DeriveWithHeightUpperBound) {
+ std::vector<Font> fonts;
+
+ fonts.push_back(gfx::Font("Arial", 18));
+ fonts.push_back(gfx::Font("Sans serif", 18));
+ fonts.push_back(gfx::Font("Symbol", 18));
+ FontList font_list = FontList(fonts);
+
+ // A smaller upper bound should derive a font list with a smaller height.
+ const int height_1 = font_list.GetHeight() - 5;
+ FontList derived_1 = font_list.DeriveWithHeightUpperBound(height_1);
+ EXPECT_LE(derived_1.GetHeight(), height_1);
+ EXPECT_LT(derived_1.GetHeight(), font_list.GetHeight());
+ EXPECT_LT(derived_1.GetFontSize(), font_list.GetFontSize());
+
+ // A larger upper bound should not change the height of the font list.
+ const int height_2 = font_list.GetHeight() + 5;
+ FontList derived_2 = font_list.DeriveWithHeightUpperBound(height_2);
+ EXPECT_LE(derived_2.GetHeight(), height_2);
+ EXPECT_EQ(font_list.GetHeight(), derived_2.GetHeight());
+ EXPECT_EQ(font_list.GetFontSize(), derived_2.GetFontSize());
+}
+
} // namespace gfx
#include "ui/gfx/font_render_params.h"
+#include <fontconfig/fontconfig.h>
+
#include "base/command_line.h"
#include "base/containers/mru_cache.h"
#include "base/hash.h"
#include "ui/gfx/linux_font_delegate.h"
#include "ui/gfx/switches.h"
-#include <fontconfig/fontconfig.h>
-
namespace gfx {
namespace {
query.style, gfx::GetFontRenderParams(query, NULL));
}
-double PlatformFontPango::underline_position() const {
- const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
- return underline_position_pixels_;
-}
-
-double PlatformFontPango::underline_thickness() const {
- const_cast<PlatformFontPango*>(this)->InitPangoMetrics();
- return underline_thickness_pixels_;
-}
-
////////////////////////////////////////////////////////////////////////////////
// PlatformFontPango, PlatformFont implementation:
pango_metrics_inited_ = false;
average_width_pixels_ = 0.0f;
- underline_position_pixels_ = 0.0f;
- underline_thickness_pixels_ = 0.0f;
}
void PlatformFontPango::InitFromPlatformFont(const PlatformFontPango* other) {
cap_height_pixels_ = other->cap_height_pixels_;
pango_metrics_inited_ = other->pango_metrics_inited_;
average_width_pixels_ = other->average_width_pixels_;
- underline_position_pixels_ = other->underline_position_pixels_;
- underline_thickness_pixels_ = other->underline_thickness_pixels_;
}
void PlatformFontPango::PaintSetup(SkPaint* paint) const {
ScopedPangoFontDescription pango_desc(GetNativeFont());
PangoFontMetrics* pango_metrics = GetPangoFontMetrics(pango_desc.get());
- underline_position_pixels_ =
- pango_font_metrics_get_underline_position(pango_metrics) /
- PANGO_SCALE;
-
- // TODO(davemoore): Come up with a better solution.
- // This is a hack, but without doing this the underlines
- // we get end up fuzzy. So we align to the midpoint of a pixel.
- underline_position_pixels_ /= 2;
-
- underline_thickness_pixels_ =
- pango_font_metrics_get_underline_thickness(pango_metrics) /
- PANGO_SCALE;
-
// First get the Pango-based width (converting from Pango units to pixels).
const double pango_width_pixels =
pango_font_metrics_get_approximate_char_width(pango_metrics) /
static void SetDefaultFontDescription(const std::string& font_description);
#endif
- // Position as an offset from the height of the drawn text, used to draw
- // an underline. This is a negative number, so the underline would be
- // drawn at y + height + underline_position.
- double underline_position() const;
- // The thickness to draw the underline.
- double underline_thickness() const;
-
// Overridden from PlatformFont:
virtual Font DeriveFont(int size_delta, int style) const OVERRIDE;
virtual int GetHeight() const OVERRIDE;
// Initializes this object as a copy of another PlatformFontPango.
void InitFromPlatformFont(const PlatformFontPango* other);
- // Potentially slow call to get pango metrics (average width, underline info).
+ // Potentially slow call to get pango metrics (average width).
void InitPangoMetrics();
// Setup a Skia context to use the current typeface.
// to compute them.
bool pango_metrics_inited_;
double average_width_pixels_;
- double underline_position_pixels_;
- double underline_thickness_pixels_;
// The default font, used for the default constructor.
static Font* default_font_;
// Pango returns the position "above the baseline". Change its sign to convert
// it to a vertical offset from the baseline.
int position = -pango_font_metrics_get_underline_position(metrics);
- pango_quantize_line_geometry(&thickness, &position);
+
// Note: pango_quantize_line_geometry() guarantees pixel boundaries, so
// PANGO_PIXELS() is safe to use.
- renderer->SetUnderlineMetrics(PANGO_PIXELS(thickness),
- PANGO_PIXELS(position));
+ pango_quantize_line_geometry(&thickness, &position);
+ int thickness_pixels = PANGO_PIXELS(thickness);
+ int position_pixels = PANGO_PIXELS(position);
+
+ // Ugly hack: make sure that underlines don't get clipped. See
+ // http://crbug.com/393117.
+ int descent_pixels = PANGO_PIXELS(pango_font_metrics_get_descent(metrics));
+ position_pixels = std::min(position_pixels,
+ descent_pixels - thickness_pixels);
+
+ renderer->SetUnderlineMetrics(thickness_pixels, position_pixels);
}
} // namespace
is_protected_(false) {
JNIEnv* env = base::android::AttachCurrentThread();
RegisterNativesIfNeeded(env);
- DCHECK(env->IsInstanceOf(surface.obj(), g_Surface_clazz));
+ DCHECK(env->IsInstanceOf(surface.obj(), Surface_clazz(env)));
j_surface_.Reset(surface);
}
} else {
ResetWindowInsets();
}
+ } else {
+ current_keyboard_bounds_ = gfx::Rect();
}
}
// case the show keyboard request is called before the height is set.
controller_->ShowKeyboard(false);
} else {
- controller_->NotifyKeyboardBoundsChanging(requested_bounds);
+ // We need to send out this notification only if keyboard is visible since
+ // keyboard window is resized even if keyboard is hidden.
+ if (controller_->keyboard_visible())
+ controller_->NotifyKeyboardBoundsChanging(requested_bounds);
}
}
display: none;
}
-.pod[auth-type='offlinePassword'].focused .password-entry-container {
+.pod[auth-type='offlinePassword'].focused .password-entry-container,
+.pod[auth-type='forceOfflinePassword'].focused .password-entry-container {
display: flex;
flex: auto;
}
flex: none;
}
+.custom-icon.faded {
+ -webkit-transition: opacity 200ms ease-in-out,
+ visibility 200ms ease-in-out;
+}
+
.custom-icon-container {
display: flex;
flex: none;
* @const
*/
var UserPodTabOrder = {
- POD_INPUT: 1, // Password input fields (and whole pods themselves).
- HEADER_BAR: 2, // Buttons on the header bar (Shutdown, Add User).
- ACTION_BOX: 3, // Action box buttons.
- PAD_MENU_ITEM: 4 // User pad menu items (Remove this user).
+ POD_INPUT: 1, // Password input fields (and whole pods themselves).
+ POD_CUSTOM_ICON: 2, // Pod custom icon next to passwrod input field.
+ HEADER_BAR: 3, // Buttons on the header bar (Shutdown, Add User).
+ ACTION_BOX: 4, // Action box buttons.
+ PAD_MENU_ITEM: 5 // User pad menu items (Remove this user).
};
/**
NUMERIC_PIN: 2,
USER_CLICK: 3,
EXPAND_THEN_USER_CLICK: 4,
+ FORCE_OFFLINE_PASSWORD: 5
};
/**
2: 'numericPin',
3: 'userClick',
4: 'expandThenUserClick',
+ 5: 'forceOfflinePassword'
};
// Focus and tab order are organized as follows:
// (1) all user pods have tab index 1 so they are traversed first;
// (2) when a user pod is activated, its tab index is set to -1 and its
// main input field gets focus and tab index 1;
- // (3) buttons on the header bar have tab index 2 so they follow user pods;
- // (4) Action box buttons have tab index 3 and follow header bar buttons;
- // (5) lastly, focus jumps to the Status Area and back to user pods.
+ // (3) if user pod custom icon is interactive, it has tab index 2 so it
+ // follows the input.
+ // (4) buttons on the header bar have tab index 3 so they follow the custom
+ // icon, or user pod if custom icon is not interactive;
+ // (5) Action box buttons have tab index 4 and follow header bar buttons;
+ // (6) lastly, focus jumps to the Status Area and back to user pods.
//
// 'Focus' event is handled by a capture handler for the whole document
// and in some cases 'mousedown' event handlers are used instead of 'click'
*/
animationResourceSize_: 0,
+ /**
+ * When {@code fadeOut} is called, the element gets hidden using fadeout
+ * animation. This is reference to the listener for transition end added to
+ * the icon element while it's fading out.
+ * @type {?function(Event)}
+ * @private
+ */
+ hideTransitionListener_: null,
+
+ /**
+ * Callback for click and 'Enter' key events that gets set if the icon is
+ * interactive.
+ * @type {?function()}
+ * @private
+ */
+ actionHandler_: null,
+
/** @override */
decorate: function() {
this.iconElement.addEventListener('mouseover',
this.hideTooltip_.bind(this, false));
this.iconElement.addEventListener('mousedown',
this.hideTooltip_.bind(this, false));
+ this.iconElement.addEventListener('click',
+ this.handleClick_.bind(this));
+ this.iconElement.addEventListener('keydown',
+ this.handleKeyDown_.bind(this));
+
+ // When the icon is focused using mouse, there should be no outline shown.
+ // Preventing default mousedown event accomplishes this.
+ this.iconElement.addEventListener('mousedown', function(e) {
+ e.preventDefault();
+ });
},
/**
* Shows the icon.
*/
show: function() {
+ this.resetHideTransitionState_();
this.hidden = false;
},
/**
- * Hides the icon. Makes sure the tooltip is hidden and animation reset.
+ * Hides the icon using a fade-out animation.
*/
- hide: function() {
+ fadeOut: function() {
+ // The icon is already being hidden.
+ if (this.iconElement.classList.contains('faded'))
+ return;
+
this.hideTooltip_(true);
- this.setAnimation(null);
- this.hidden = true;
+ this.iconElement.classList.add('faded');
+ this.hideTransitionListener_ = this.hide_.bind(this);
+ this.iconElement.addEventListener('webkitTransitionEnd',
+ this.hideTransitionListener_);
+ ensureTransitionEndEvent(this.iconElement, 200);
},
/**
},
/**
+ * Sets up icon tabIndex attribute and handler for click and 'Enter' key
+ * down events.
+ * @param {?function()} callback If icon should be interactive, the
+ * function to get called on click and 'Enter' key down events. Should
+ * be null to make the icon non interactive.
+ */
+ setInteractive: function(callback) {
+ // Update tabIndex property if needed.
+ if (!!this.actionHandler_ != !!callback) {
+ if (callback) {
+ this.iconElement.setAttribute('tabIndex',
+ UserPodTabOrder.POD_CUSTOM_ICON);
+ } else {
+ this.iconElement.removeAttribute('tabIndex');
+ }
+ }
+
+ // Set the new action handler.
+ this.actionHandler_ = callback;
+ },
+
+ /**
+ * Hides the icon. Makes sure the tooltip is hidden and animation reset.
+ * @private
+ */
+ hide_: function() {
+ this.hideTooltip_(true);
+ this.hidden = true;
+ this.setAnimation(null);
+ this.setInteractive(null);
+ this.resetHideTransitionState_();
+ },
+
+ /**
+ * Ensures the icon's transition state potentially set by a call to
+ * {@code fadeOut} is cleared.
+ * @private
+ */
+ resetHideTransitionState_: function() {
+ if (this.hideTransitionListener_) {
+ this.iconElement.removeEventListener('webkitTransitionEnd',
+ this.hideTransitionListener_);
+ this.hideTransitionListener_ = null;
+ }
+ this.iconElement.classList.toggle('faded', false);
+ },
+
+ /**
+ * Handles click event on the icon element. No-op if
+ * {@code this.actionHandler_} is not set.
+ * @param {Event} e The click event.
+ * @private
+ */
+ handleClick_: function(e) {
+ if (!this.actionHandler_)
+ return;
+ this.actionHandler_();
+ stopEventPropagation(e);
+ },
+
+ /**
+ * Handles key down event on the icon element. Only 'Enter' key is handled.
+ * No-op if {@code this.actionHandler_} is not set.
+ * @param {Event} e The key down event.
+ * @private
+ */
+ handleKeyDown_: function(e) {
+ if (!this.actionHandler_ || e.keyIdentifier != 'Enter')
+ return;
+ this.actionHandler_(e);
+ stopEventPropagation(e);
+ },
+
+ /**
* Called when mouse enters the icon. It sets timeout for showing the
* tooltip.
* @private
},
/**
- * Shows the current tooltip, if one is set.
+ * Shows the current tooltip if one is set.
* @private
*/
showTooltip_: function() {
* @type {bool}
*/
get isAuthTypePassword() {
- return this.authType_ == AUTH_TYPE.OFFLINE_PASSWORD;
+ return this.authType_ == AUTH_TYPE.OFFLINE_PASSWORD ||
+ this.authType_ == AUTH_TYPE.FORCE_OFFLINE_PASSWORD;
},
/**
pod.customIconElement.setSize(icon.size || {width: 0, height: 0});
pod.customIconElement.setAnimation(icon.animation || null);
pod.customIconElement.setOpacity(icon.opacity || 100);
+ if (icon.hardlockOnClick) {
+ pod.customIconElement.setInteractive(
+ this.hardlockUserPod_.bind(this, username));
+ } else {
+ pod.customIconElement.setInteractive(null);
+ }
pod.customIconElement.show();
// This has to be called after |show| in case the tooltip should be shown
// immediatelly.
},
/**
+ * Hard-locks user pod for the user. If user pod is hard-locked, it can be
+ * only unlocked using password, and the authentication type cannot be
+ * changed.
+ * @param {!string} username The user's username.
+ * @private
+ */
+ hardlockUserPod_: function(username) {
+ chrome.send('hardlockPod', [username]);
+ },
+
+ /**
* Hides the custom icon in the user pod added by showUserPodCustomIcon().
* @param {string} username Username of pod to remove button
*/
return;
}
- pod.customIconElement.hide();
+ pod.customIconElement.fadeOut();
},
/**
<structure type="chrome_scaled_image" name="IDR_CLOSE_2_H" file="close_2_hover.png" />
<structure type="chrome_scaled_image" name="IDR_CLOSE_2_MASK" file="close_2_mask.png" />
<structure type="chrome_scaled_image" name="IDR_CLOSE_2_P" file="close_2_pressed.png" />
+ <structure type="chrome_scaled_image" name="IDR_CLOSE_3_MASK" file="common/close_3_mask.png" />
<structure type="chrome_scaled_image" name="IDR_CLOSE_DIALOG" file="close_dialog.png" />
<structure type="chrome_scaled_image" name="IDR_CLOSE_DIALOG_H" file="close_dialog_hover.png" />
<structure type="chrome_scaled_image" name="IDR_CLOSE_DIALOG_P" file="close_dialog_pressed.png" />
<structure type="chrome_scaled_image" name="IDR_NOTIFICATION_SETTINGS_PRESSED" file="common/notification_settings_pressed.png"/>
</if>
<if expr="not is_android and not is_ios">
+ <structure type="chrome_scaled_image" name="IDR_NTP_DEFAULT_FAVICON" file="common/ntp_default_favicon.png" />
+ </if>
+ <if expr="not is_android and not is_ios">
<structure type="chrome_scaled_image" name="IDR_OOBE_ACTION_BOX_BUTTON_HOVER" file="cros/action_box_button_hover.png" />
<structure type="chrome_scaled_image" name="IDR_OOBE_ACTION_BOX_BUTTON_NORMAL" file="cros/action_box_button_normal.png" />
<structure type="chrome_scaled_image" name="IDR_OOBE_ACTION_BOX_BUTTON_PRESSED" file="cros/action_box_button_pressed.png" />
<!DOCTYPE translationbundle>
<translationbundle lang="ja">
<translation id="IDS_MINIMUM_UI_FONT_SIZE">10</translation>
-<translation id="IDS_UI_FONT_FAMILY_CROS">MotoyaG04Gothic,Noto Sans UI,IPAPGothic,ui-sans, 13px</translation>
+<translation id="IDS_UI_FONT_FAMILY_CROS">Noto Sans UI,Noto Sans CJK Japanese,ui-sans, 13px</translation>
<if expr="is_win">
<translation id="IDS_WEB_FONT_FAMILY">'Segoe UI',Arial,Meiryo,'MS PGothic',sans-serif</translation>
<translation id="IDS_WEB_FONT_FAMILY_XP">Arial,Meiryo,'MS PGothic',sans-serif</translation>
<translation id="IDS_WEB_FONT_FAMILY">VL PGothic,Sazanami Gothic,Kochi Gothic,sans-serif</translation>
</if>
<if expr="chromeos and _google_chrome">
- <translation id="IDS_WEB_FONT_FAMILY">MotoyaG04Gothic, Noto Sans UI, sans-serif</translation>
-</if>
-<if expr="chromeos and not _google_chrome">
- <translation id="IDS_WEB_FONT_FAMILY">Noto Sans UI, IPAPGothic, sans-serif</translation>
+ <translation id="IDS_WEB_FONT_FAMILY">Noto Sans UI, Noto Sans CJK Japanese, sans-serif</translation>
</if>
</translationbundle>
<!DOCTYPE translationbundle>
<translationbundle lang="ko">
<translation id="IDS_MINIMUM_UI_FONT_SIZE">10</translation>
-<translation id="IDS_UI_FONT_FAMILY_CROS">Noto Sans UI,NanumGothic,ui-sans, 13px</translation>
+<translation id="IDS_UI_FONT_FAMILY_CROS">Noto Sans UI,Noto Sans CJK Korean,ui-sans, 13px</translation>
<if expr="is_win">
<translation id="IDS_WEB_FONT_FAMILY">'Segoe UI',Arial,'Malgun Gothic',Gulim,sans-serif</translation>
<translation id="IDS_WEB_FONT_FAMILY_XP">Arial,'Malgun Gothic',Gulim,sans-serif</translation>
<translation id="IDS_WEB_FONT_FAMILY">NanumGothic,UnDotum,Baekmuk Gulim,sans-serif</translation>
</if>
<if expr="chromeos">
- <translation id="IDS_WEB_FONT_FAMILY">Noto Sans UI, NanumGothic, sans-serif</translation>
+ <translation id="IDS_WEB_FONT_FAMILY">Noto Sans UI, Noto Sans CJK Korean, sans-serif</translation>
</if>
</translationbundle>
<!DOCTYPE translationbundle>
<translationbundle lang="zh-CN">
<translation id="IDS_MINIMUM_UI_FONT_SIZE">10</translation>
-<translation id="IDS_UI_FONT_FAMILY_CROS">Noto Sans UI,MYingHeiGB18030,MYingHeiB5HK,ui-sans, 13px</translation>
+<translation id="IDS_UI_FONT_FAMILY_CROS">Noto Sans UI,Noto Sans CJK Simplified Chinese,ui-sans, 13px</translation>
<if expr="is_win">
<translation id="IDS_WEB_FONT_FAMILY">'Segoe UI',Arial,'Microsoft Yahei',Simsun,sans-serif</translation>
<translation id="IDS_WEB_FONT_FAMILY_XP">Arial,'Microsoft Yahei',Simsun,sans-serif</translation>
<if expr="is_macosx or is_ios">
<translation id="IDS_WEB_FONT_FAMILY">Helvetica,STHeiti,sans-serif</translation>
</if>
-<if expr="chromeos and _google_chrome">
- <translation id="IDS_WEB_FONT_FAMILY">Noto Sans UI, MYingHeiGB18030, MYingHeiB5HK, sans-serif</translation>
-</if>
-<if expr="chromeos and not _google_chrome">
- <translation id="IDS_WEB_FONT_FAMILY">Noto Sans UI, Droid Sans Fallback, sans-serif</translation>
+<if expr="chromeos">
+ <translation id="IDS_WEB_FONT_FAMILY">Noto Sans UI, Noto Sans CJK Simplified Chinese, sans-serif</translation>
</if>
</translationbundle>
<!DOCTYPE translationbundle>
<translationbundle lang="zh-TW">
<translation id="IDS_MINIMUM_UI_FONT_SIZE">10</translation>
-<translation id="IDS_UI_FONT_FAMILY_CROS">Noto Sans UI,MYingHeiB5HK,MYingHeiGB18030,ui-sans, 13px</translation>
+<translation id="IDS_UI_FONT_FAMILY_CROS">Noto Sans UI,Noto Sans CJK Traditional Chinese,ui-sans, 13px</translation>
<if expr="is_win">
<translation id="IDS_WEB_FONT_FAMILY">'Segoe UI',Arial,'Microsoft Jhenghei',PMingLiu,sans-serif</translation>
<translation id="IDS_WEB_FONT_FAMILY_XP">Arial,'Microsoft Jhenghei',PMingLiu,sans-serif</translation>
<translation id="IDS_WEB_FONT_FAMILY">Helvetica,Heiti TC,sans-serif</translation>
</if>
<if expr="chromeos and _google_chrome">
- <translation id="IDS_WEB_FONT_FAMILY">Noto Sans UI, MYingHeiB5HK, MYingHeiGB18030, sans-serif</translation>
-</if>
-<if expr="chromeos and not _google_chrome">
- <translation id="IDS_WEB_FONT_FAMILY">Noto Sans UI, Droid Sans Fallback, sans-serif</translation>
+ <translation id="IDS_WEB_FONT_FAMILY">Noto Sans UI, Noto Sans CJK Traditional Chinese, sans-serif</translation>
</if>
</translationbundle>
#include <algorithm>
#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
#include "grit/ui_resources.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
-#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/skia_util.h"
#include "ui/views/painter.h"
namespace internal {
-// A helper that combines each border image-set painter with arrows and metrics.
-struct BorderImages {
- BorderImages(const int border_image_ids[],
- const int arrow_image_ids[],
- int border_interior_thickness,
- int arrow_interior_thickness,
- int corner_radius);
-
- scoped_ptr<Painter> border_painter;
- gfx::ImageSkia left_arrow;
- gfx::ImageSkia top_arrow;
- gfx::ImageSkia right_arrow;
- gfx::ImageSkia bottom_arrow;
-
- // The thickness of border and arrow images and their interior areas.
- // Thickness is the width of left/right and the height of top/bottom images.
- // The interior is measured without including stroke or shadow pixels.
- int border_thickness;
- int border_interior_thickness;
- int arrow_thickness;
- int arrow_interior_thickness;
- // The corner radius of the bubble's rounded-rect interior area.
- int corner_radius;
-};
-
BorderImages::BorderImages(const int border_image_ids[],
const int arrow_image_ids[],
int border_interior_thickness,
}
}
+BorderImages::~BorderImages() {}
+
} // namespace internal
namespace {
-// The border and arrow stroke size used in image assets, in pixels.
-const int kStroke = 1;
-
// Bubble border and arrow image resource ids. They don't use the IMAGE_GRID
// macro because there is no center image.
const int kNoShadowImages[] = {
} // namespace
+const int BubbleBorder::kStroke = 1;
+
BubbleBorder::BubbleBorder(Arrow arrow, Shadow shadow, SkColor color)
: arrow_(arrow),
arrow_offset_(0),
std::max(images_->arrow_thickness + images_->border_interior_thickness,
images_->border_thickness);
// Only take arrow image sizes into account when the bubble tip is shown.
- if (arrow_paint_type_ == PAINT_TRANSPARENT || !has_arrow(arrow_))
+ if (arrow_paint_type_ == PAINT_NONE || !has_arrow(arrow_))
size.SetToMax(gfx::Size(min, min));
else if (is_arrow_on_horizontal(arrow_))
size.SetToMax(gfx::Size(min_with_arrow_width, min_with_arrow_thickness));
canvas->DrawPath(path, paint);
}
+internal::BorderImages* BubbleBorder::GetImagesForTest() const {
+ return images_;
+}
+
void BubbleBackground::Paint(gfx::Canvas* canvas, views::View* view) const {
if (border_->shadow() == BubbleBorder::NO_SHADOW_OPAQUE_BORDER)
canvas->DrawColor(border_->background_color());
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/image/image_skia.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
namespace gfx {
-class ImageSkia;
class Rect;
}
namespace views {
+class Painter;
namespace internal {
-struct BorderImages;
-}
+
+// A helper that combines each border image-set painter with arrows and metrics.
+struct BorderImages {
+ BorderImages(const int border_image_ids[],
+ const int arrow_image_ids[],
+ int border_interior_thickness,
+ int arrow_interior_thickness,
+ int corner_radius);
+ virtual ~BorderImages();
+
+ scoped_ptr<Painter> border_painter;
+ gfx::ImageSkia left_arrow;
+ gfx::ImageSkia top_arrow;
+ gfx::ImageSkia right_arrow;
+ gfx::ImageSkia bottom_arrow;
+
+ // The thickness of border and arrow images and their interior areas.
+ // Thickness is the width of left/right and the height of top/bottom images.
+ // The interior is measured without including stroke or shadow pixels.
+ int border_thickness;
+ int border_interior_thickness;
+ int arrow_thickness;
+ int arrow_interior_thickness;
+ // The corner radius of the bubble's rounded-rect interior area.
+ int corner_radius;
+};
+
+} // namespace internal
// Renders a border, with optional arrow, and a custom dropshadow.
// This can be used to produce floating "bubble" objects with rounded corners.
virtual gfx::Size GetMinimumSize() const OVERRIDE;
private:
+ FRIEND_TEST_ALL_PREFIXES(BubbleBorderTest, GetSizeForContentsSizeTest);
+ FRIEND_TEST_ALL_PREFIXES(BubbleBorderTest, GetBoundsOriginTest);
+
+ // The border and arrow stroke size used in image assets, in pixels.
+ static const int kStroke;
+
gfx::Size GetSizeForContentsSize(const gfx::Size& contents_size) const;
gfx::ImageSkia* GetArrowImage() const;
gfx::Rect GetArrowRect(const gfx::Rect& bounds) const;
void DrawArrow(gfx::Canvas* canvas, const gfx::Rect& arrow_bounds) const;
+ internal::BorderImages* GetImagesForTest() const;
+
Arrow arrow_;
int arrow_offset_;
ArrowPaintType arrow_paint_type_;
#include "ui/views/bubble/bubble_border.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/stringprintf.h"
#include "ui/views/test/views_test_base.h"
namespace views {
-typedef ViewsTestBase BubbleBorderTest;
+typedef views::ViewsTestBase BubbleBorderTest;
TEST_F(BubbleBorderTest, GetMirroredArrow) {
// Horizontal mirroring.
EXPECT_FALSE(BubbleBorder::is_arrow_at_center(BubbleBorder::FLOAT));
}
-TEST_F(BubbleBorderTest, TestMinimalSize) {
- gfx::Rect anchor = gfx::Rect(100, 100, 20, 20);
- gfx::Size contents = gfx::Size(10, 10);
- BubbleBorder b1(BubbleBorder::RIGHT_TOP, BubbleBorder::NO_SHADOW, 0);
-
- // The height should be much bigger then the requested size + border and
- // padding since it needs to be able to include the tip bitmap.
- gfx::Rect visible_tip_1 = b1.GetBounds(anchor, contents);
- EXPECT_GE(visible_tip_1.height(), 30);
- EXPECT_LE(visible_tip_1.width(), 30);
-
- // With the tip being invisible the height should now be much smaller.
- b1.set_paint_arrow(BubbleBorder::PAINT_TRANSPARENT);
- gfx::Rect invisible_tip_1 = b1.GetBounds(anchor, contents);
- EXPECT_LE(invisible_tip_1.height(), 30);
- EXPECT_LE(invisible_tip_1.width(), 30);
-
- // When the orientation of the tip changes, the above mentioned tests need to
- // be reverse for width and height.
- BubbleBorder b2(BubbleBorder::TOP_RIGHT, BubbleBorder::NO_SHADOW, 0);
-
- // The width should be much bigger then the requested size + border and
- // padding since it needs to be able to include the tip bitmap.
- gfx::Rect visible_tip_2 = b2.GetBounds(anchor, contents);
- EXPECT_GE(visible_tip_2.width(), 30);
- EXPECT_LE(visible_tip_2.height(), 30);
-
- // With the tip being invisible the width should now be much smaller.
- b2.set_paint_arrow(BubbleBorder::PAINT_TRANSPARENT);
- gfx::Rect invisible_tip_2 = b2.GetBounds(anchor, contents);
- EXPECT_LE(invisible_tip_2.width(), 30);
- EXPECT_LE(invisible_tip_2.height(), 30);
+TEST_F(BubbleBorderTest, GetSizeForContentsSizeTest) {
+ views::BubbleBorder border(BubbleBorder::NONE,
+ BubbleBorder::NO_SHADOW,
+ SK_ColorWHITE);
+
+ const views::internal::BorderImages* kImages = border.GetImagesForTest();
+
+ // kSmallSize is smaller than the minimum allowable size and does not
+ // contribute to the resulting size.
+ const gfx::Size kSmallSize = gfx::Size(1, 2);
+ // kMediumSize is larger than the minimum allowable size and contributes to
+ // the resulting size.
+ const gfx::Size kMediumSize = gfx::Size(50, 60);
+
+ const gfx::Size kSmallHorizArrow(
+ 2 * kImages->border_thickness + kImages->top_arrow.width(),
+ kImages->border_thickness + kImages->arrow_thickness +
+ kImages->border_interior_thickness);
+
+ const gfx::Size kSmallVertArrow(kSmallHorizArrow.height(),
+ kSmallHorizArrow.width());
+
+ const gfx::Size kSmallNoArrow(2 * kImages->border_thickness,
+ 2 * kImages->border_thickness);
+
+ const gfx::Size kMediumHorizArrow(
+ kMediumSize.width() + 2 * border.GetBorderThickness(),
+ kMediumSize.height() + border.GetBorderThickness() +
+ kImages->arrow_thickness);
+
+ const gfx::Size kMediumVertArrow(
+ kMediumSize.width() + border.GetBorderThickness() +
+ kImages->arrow_thickness,
+ kMediumSize.height() + 2 * border.GetBorderThickness());
+
+ const gfx::Size kMediumNoArrow(
+ kMediumSize.width() + 2 * border.GetBorderThickness(),
+ kMediumSize.height() + 2 * border.GetBorderThickness());
+
+ struct TestCase {
+ BubbleBorder::Arrow arrow;
+ gfx::Size content;
+ gfx::Size expected_with_arrow;
+ gfx::Size expected_without_arrow;
+ };
+
+ TestCase cases[] = {
+ // Content size: kSmallSize
+ { BubbleBorder::TOP_LEFT, kSmallSize, kSmallHorizArrow, kSmallNoArrow },
+ { BubbleBorder::TOP_CENTER, kSmallSize, kSmallHorizArrow, kSmallNoArrow },
+ { BubbleBorder::TOP_RIGHT, kSmallSize, kSmallHorizArrow, kSmallNoArrow },
+ { BubbleBorder::BOTTOM_LEFT, kSmallSize, kSmallHorizArrow, kSmallNoArrow },
+ { BubbleBorder::BOTTOM_CENTER, kSmallSize, kSmallHorizArrow,
+ kSmallNoArrow },
+ { BubbleBorder::BOTTOM_RIGHT, kSmallSize, kSmallHorizArrow, kSmallNoArrow },
+ { BubbleBorder::LEFT_TOP, kSmallSize, kSmallVertArrow, kSmallNoArrow },
+ { BubbleBorder::LEFT_CENTER, kSmallSize, kSmallVertArrow, kSmallNoArrow },
+ { BubbleBorder::LEFT_BOTTOM, kSmallSize, kSmallVertArrow, kSmallNoArrow },
+ { BubbleBorder::RIGHT_TOP, kSmallSize, kSmallVertArrow, kSmallNoArrow },
+ { BubbleBorder::RIGHT_CENTER, kSmallSize, kSmallVertArrow, kSmallNoArrow },
+ { BubbleBorder::RIGHT_BOTTOM, kSmallSize, kSmallVertArrow, kSmallNoArrow },
+ { BubbleBorder::NONE, kSmallSize, kSmallNoArrow, kSmallNoArrow },
+ { BubbleBorder::FLOAT, kSmallSize, kSmallNoArrow, kSmallNoArrow },
+
+ // Content size: kMediumSize
+ { BubbleBorder::TOP_LEFT, kMediumSize, kMediumHorizArrow, kMediumNoArrow },
+ { BubbleBorder::TOP_CENTER, kMediumSize, kMediumHorizArrow,
+ kMediumNoArrow },
+ { BubbleBorder::TOP_RIGHT, kMediumSize, kMediumHorizArrow, kMediumNoArrow },
+ { BubbleBorder::BOTTOM_LEFT, kMediumSize, kMediumHorizArrow,
+ kMediumNoArrow },
+ { BubbleBorder::BOTTOM_CENTER, kMediumSize, kMediumHorizArrow,
+ kMediumNoArrow },
+ { BubbleBorder::BOTTOM_RIGHT, kMediumSize, kMediumHorizArrow,
+ kMediumNoArrow },
+ { BubbleBorder::LEFT_TOP, kMediumSize, kMediumVertArrow, kMediumNoArrow },
+ { BubbleBorder::LEFT_CENTER, kMediumSize, kMediumVertArrow,
+ kMediumNoArrow },
+ { BubbleBorder::LEFT_BOTTOM, kMediumSize, kMediumVertArrow,
+ kMediumNoArrow },
+ { BubbleBorder::RIGHT_TOP, kMediumSize, kMediumVertArrow, kMediumNoArrow },
+ { BubbleBorder::RIGHT_CENTER, kMediumSize, kMediumVertArrow,
+ kMediumNoArrow },
+ { BubbleBorder::RIGHT_BOTTOM, kMediumSize, kMediumVertArrow,
+ kMediumNoArrow },
+ { BubbleBorder::NONE, kMediumSize, kMediumNoArrow, kMediumNoArrow },
+ { BubbleBorder::FLOAT, kMediumSize, kMediumNoArrow, kMediumNoArrow }
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ SCOPED_TRACE(base::StringPrintf("i=%d arrow=%d",
+ static_cast<int>(i), cases[i].arrow));
+
+ border.set_arrow(cases[i].arrow);
+
+ border.set_paint_arrow(BubbleBorder::PAINT_NORMAL);
+ EXPECT_EQ(cases[i].expected_with_arrow,
+ border.GetSizeForContentsSize(cases[i].content));
+
+ border.set_paint_arrow(BubbleBorder::PAINT_TRANSPARENT);
+ EXPECT_EQ(cases[i].expected_with_arrow,
+ border.GetSizeForContentsSize(cases[i].content));
+
+ border.set_paint_arrow(BubbleBorder::PAINT_NONE);
+ EXPECT_EQ(cases[i].expected_without_arrow,
+ border.GetSizeForContentsSize(cases[i].content));
+ }
}
+TEST_F(BubbleBorderTest, GetBoundsOriginTest) {
+ views::BubbleBorder border(BubbleBorder::TOP_LEFT,
+ BubbleBorder::NO_SHADOW,
+ SK_ColorWHITE);
+
+ const gfx::Rect kAnchor(100, 100, 20, 30);
+ const gfx::Size kContentSize(50, 60);
+
+ const views::internal::BorderImages* kImages = border.GetImagesForTest();
+
+ border.set_arrow(BubbleBorder::TOP_LEFT);
+ const gfx::Size kTotalSizeWithHorizArrow =
+ border.GetSizeForContentsSize(kContentSize);
+
+ border.set_arrow(BubbleBorder::RIGHT_BOTTOM);
+ const gfx::Size kTotalSizeWithVertArrow =
+ border.GetSizeForContentsSize(kContentSize);
+
+ border.set_arrow(BubbleBorder::NONE);
+ const gfx::Size kTotalSizeWithNoArrow =
+ border.GetSizeForContentsSize(kContentSize);
+
+ const int kBorderThickness = border.GetBorderThickness();
+
+ const int kArrowOffsetForHorizCenter = kTotalSizeWithHorizArrow.width() / 2;
+ const int kArrowOffsetForVertCenter = kTotalSizeWithVertArrow.height() / 2;
+ const int kArrowOffsetForNotCenter =
+ kImages->border_thickness + (kImages->top_arrow.width() / 2);
+
+ const int kArrowSize =
+ kImages->arrow_interior_thickness + BubbleBorder::kStroke -
+ kImages->arrow_thickness;
+
+ const int kTopHorizArrowY = kAnchor.y() + kAnchor.height() + kArrowSize;
+ const int kBottomHorizArrowY =
+ kAnchor.y() - kArrowSize - kTotalSizeWithHorizArrow.height();
+
+ const int kLeftVertArrowX = kAnchor.x() + kAnchor.width() + kArrowSize;
+ const int kRightVertArrowX =
+ kAnchor.x() - kArrowSize - kTotalSizeWithVertArrow.width();
+
+ struct TestCase {
+ BubbleBorder::Arrow arrow;
+ BubbleBorder::BubbleAlignment alignment;
+ int expected_x;
+ int expected_y;
+ };
+
+ TestCase cases[] = {
+ // Horizontal arrow tests.
+ { BubbleBorder::TOP_LEFT, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
+ kAnchor.CenterPoint().x() - kArrowOffsetForNotCenter, kTopHorizArrowY },
+ { BubbleBorder::TOP_LEFT, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE,
+ kAnchor.x() + BubbleBorder::kStroke - kBorderThickness, kTopHorizArrowY },
+ { BubbleBorder::TOP_CENTER, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
+ kAnchor.CenterPoint().x() - kArrowOffsetForHorizCenter, kTopHorizArrowY },
+ { BubbleBorder::BOTTOM_RIGHT, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
+ kAnchor.CenterPoint().x() + kArrowOffsetForNotCenter -
+ kTotalSizeWithHorizArrow.width(), kBottomHorizArrowY },
+ { BubbleBorder::BOTTOM_RIGHT, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE,
+ kAnchor.x() + kAnchor.width() - kTotalSizeWithHorizArrow.width() +
+ kBorderThickness - BubbleBorder::kStroke, kBottomHorizArrowY },
+
+ // Vertical arrow tests.
+ { BubbleBorder::LEFT_TOP, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
+ kLeftVertArrowX, kAnchor.CenterPoint().y() - kArrowOffsetForNotCenter },
+ { BubbleBorder::LEFT_TOP, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE,
+ kLeftVertArrowX, kAnchor.y() + BubbleBorder::kStroke - kBorderThickness },
+ { BubbleBorder::LEFT_CENTER, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
+ kLeftVertArrowX, kAnchor.CenterPoint().y() - kArrowOffsetForVertCenter },
+ { BubbleBorder::RIGHT_BOTTOM, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
+ kRightVertArrowX, kAnchor.CenterPoint().y() + kArrowOffsetForNotCenter -
+ kTotalSizeWithVertArrow.height() },
+ { BubbleBorder::RIGHT_BOTTOM, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE,
+ kRightVertArrowX, kAnchor.y() + kAnchor.height() -
+ kTotalSizeWithVertArrow.height() + kBorderThickness -
+ BubbleBorder::kStroke },
+
+ // No arrow tests.
+ { BubbleBorder::NONE, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
+ kAnchor.x() + (kAnchor.width() - kTotalSizeWithNoArrow.width()) / 2,
+ kAnchor.y() + kAnchor.height() },
+ { BubbleBorder::FLOAT, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
+ kAnchor.x() + (kAnchor.width() - kTotalSizeWithNoArrow.width()) / 2,
+ kAnchor.y() + (kAnchor.height() - kTotalSizeWithNoArrow.height()) / 2 },
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ SCOPED_TRACE(base::StringPrintf("i=%d arrow=%d alignment=%d",
+ static_cast<int>(i), cases[i].arrow, cases[i].alignment));
+ border.set_arrow(cases[i].arrow);
+ border.set_alignment(cases[i].alignment);
+
+ gfx::Point origin = border.GetBounds(kAnchor, kContentSize).origin();
+ EXPECT_EQ(cases[i].expected_x, origin.x());
+ EXPECT_EQ(cases[i].expected_y, origin.y());
+ }
+}
} // namespace views
virtual bool Contains(const gfx::Point& screen_point,
MouseEventType type) OVERRIDE;
private:
-
DISALLOW_COPY_AND_ASSIGN(MouseMoveDetectorHost);
};
void TrayBubbleView::SetArrowPaintType(
views::BubbleBorder::ArrowPaintType paint_type) {
bubble_border_->set_paint_arrow(paint_type);
+ UpdateBubble();
}
gfx::Insets TrayBubbleView::GetBorderInsets() const {
void DesktopWindowTreeHostWin::SetShape(gfx::NativeRegion native_region) {
if (native_region) {
- message_handler_->SetRegion(gfx::CreateHRGNFromSkRegion(*native_region));
+ // TODO(wez): This would be a lot simpler if we were passed an SkPath.
+ // See crbug.com/410593.
+ gfx::NativeRegion shape = native_region;
+ SkRegion device_region;
+ if (gfx::IsInHighDPIMode()) {
+ shape = &device_region;
+ const float& scale = gfx::GetDPIScale();
+ std::vector<SkIRect> rects;
+ for (SkRegion::Iterator it(*native_region); !it.done(); it.next()) {
+ const SkIRect& rect = it.rect();
+ SkRect scaled_rect =
+ SkRect::MakeLTRB(rect.left() * scale, rect.top() * scale,
+ rect.right() * scale, rect.bottom() * scale);
+ SkIRect rounded_scaled_rect;
+ scaled_rect.roundOut(&rounded_scaled_rect);
+ rects.push_back(rounded_scaled_rect);
+ }
+ if (!rects.empty())
+ device_region.setRects(&rects[0], rects.size());
+ }
+
+ message_handler_->SetRegion(gfx::CreateHRGNFromSkRegion(*shape));
} else {
message_handler_->SetRegion(NULL);
}
const size_t commit_size,
size_t* allocated) {
DCHECK(commit_size <= requested_size);
- DCHECK(current_allocation_block_index_ < allocation_list_.length());
- if (requested_size > allocation_list_[current_allocation_block_index_].size) {
+ DCHECK(allocation_list_.length() == 0 ||
+ current_allocation_block_index_ < allocation_list_.length());
+ if (allocation_list_.length() == 0 ||
+ requested_size > allocation_list_[current_allocation_block_index_].size) {
// Find an allocation block large enough.
if (!GetNextAllocationBlock(requested_size)) return NULL;
}
allocation_list_[current_allocation_block_index_].size -= *allocated;
if (*allocated == current.size) {
// This block is used up, get the next one.
- if (!GetNextAllocationBlock(0)) return NULL;
+ GetNextAllocationBlock(0);
}
return current.start;
}
HValue* value, Representation representation) {
if (FLAG_fold_constants && value->IsConstant()) {
HConstant* c = HConstant::cast(value);
- if (c->HasNumberValue()) {
- double double_res = c->DoubleValue();
- if (representation.IsDouble()) {
- return HConstant::New(zone, context, double_res);
-
- } else if (representation.CanContainDouble(double_res)) {
- return HConstant::New(zone, context,
- static_cast<int32_t>(double_res),
- representation);
- }
- }
+ c = c->CopyToRepresentation(representation, zone);
+ if (c != NULL) return c;
}
return new(zone) HForceRepresentation(value, representation);
}
Push(graph()->GetConstantMinus1());
if (IsFastDoubleElementsKind(kind) || IsFastSmiElementsKind(kind)) {
+ // Make sure that we can actually compare numbers correctly below, see
+ // https://code.google.com/p/chromium/issues/detail?id=407946 for details.
+ search_element = AddUncasted<HForceRepresentation>(
+ search_element, IsFastSmiElementsKind(kind) ? Representation::Smi()
+ : Representation::Double());
+
LoopBuilder loop(this, context(), direction);
{
HValue* index = loop.BeginBody(initial, terminating, token);
elements, index, static_cast<HValue*>(NULL),
kind, ALLOW_RETURN_HOLE);
IfBuilder if_issame(this);
- if (IsFastDoubleElementsKind(kind)) {
- if_issame.If<HCompareNumericAndBranch>(
- element, search_element, Token::EQ_STRICT);
- } else {
- if_issame.If<HCompareObjectEqAndBranch>(element, search_element);
- }
+ if_issame.If<HCompareNumericAndBranch>(element, search_element,
+ Token::EQ_STRICT);
if_issame.Then();
{
Drop(1);
namespace v8 {
namespace internal {
-inline bool Representation::CanContainDouble(double value) {
- if (IsDouble() || is_more_general_than(Representation::Double())) {
- return true;
- }
- if (IsInt32Double(value)) {
- if (IsInteger32()) return true;
- if (IsSmi()) return Smi::IsValid(static_cast<int32_t>(value));
- }
- return false;
-}
-
-
Representation Representation::FromType(Type* type) {
DisallowHeapAllocation no_allocation;
if (type->Is(Type::None())) return Representation::None();
return other.is_more_general_than(*this) || other.Equals(*this);
}
- bool CanContainDouble(double value);
-
Representation generalize(Representation other) {
if (other.fits_into(*this)) return *this;
if (other.is_more_general_than(*this)) return other;
#define MAJOR_VERSION 3
#define MINOR_VERSION 28
#define BUILD_NUMBER 71
-#define PATCH_LEVEL 5
+#define PATCH_LEVEL 9
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
#define IS_CANDIDATE_VERSION 0
}
+TEST(Regress3540) {
+ Isolate* isolate = CcTest::i_isolate();
+ isolate->InitializeLoggingAndCounters();
+ Heap* heap = isolate->heap();
+ CHECK(heap->ConfigureHeapDefault());
+ MemoryAllocator* memory_allocator = new MemoryAllocator(isolate);
+ CHECK(
+ memory_allocator->SetUp(heap->MaxReserved(), heap->MaxExecutableSize()));
+ TestMemoryAllocatorScope test_allocator_scope(isolate, memory_allocator);
+ CodeRange* code_range = new CodeRange(isolate);
+ const size_t code_range_size = 4 * MB;
+ if (!code_range->SetUp(code_range_size)) return;
+ size_t allocated_size;
+ Address result;
+ for (int i = 0; i < 5; i++) {
+ result = code_range->AllocateRawMemory(
+ code_range_size - MB, code_range_size - MB, &allocated_size);
+ CHECK((result != NULL) == (i == 0));
+ }
+}
+
+
static unsigned int Pseudorandom() {
static uint32_t lo = 2345;
lo = 18273 * (lo & 0xFFFFF) + (lo >> 16);
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax
+
+function f(n) { return [0].indexOf((n - n) + 0); }
+
+assertEquals(0, f(.1));
+assertEquals(0, f(.1));
+%OptimizeFunctionOnNextCall(f);
+assertEquals(0, f(.1));
--- /dev/null
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax
+
+// Test push double as tagged.
+var a = [{}];
+function f(a) {
+ a.push(Infinity);
+}
+
+f(a);
+f(a);
+f(a);
+%OptimizeFunctionOnNextCall(f);
+f(a);
+assertEquals([{}, Infinity, Infinity, Infinity, Infinity], a);
['simulator', {
'function-apply-aliased': [SKIP],
}], # 'simulator'
+['arch == arm and simulator_run == True', {
+ 'dfg-int-overflow-in-loop': [SKIP],
+}], # 'arch == arm and simulator_run == True'
['arch == arm64 and simulator_run == True', {
'dfg-int-overflow-in-loop': [SKIP],
}], # 'arch == arm64 and simulator_run == True'
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+from collections import OrderedDict
import itertools
import multiprocessing
import optparse
ARCH_GUESS = utils.DefaultArch()
DEFAULT_TESTS = ["mjsunit", "fuzz-natives", "base-unittests",
"cctest", "compiler-unittests", "message", "preparser"]
+
+# Map of test name synonyms to lists of test suites. Should be ordered by
+# expected runtimes (suites with slow test cases first). These groups are
+# invoked in seperate steps on the bots.
+TEST_MAP = {
+ "default": [
+ "mjsunit",
+ "fuzz-natives",
+ "cctest",
+ "message",
+ "preparser",
+ ],
+ "optimize_for_size": [
+ "mjsunit",
+ "cctest",
+ "webkit",
+ ],
+ "unittests": [
+ "compiler-unittests",
+ "heap-unittests",
+ "base-unittests",
+ "libplatform-unittests",
+ ],
+}
+
TIMEOUT_DEFAULT = 60
TIMEOUT_SCALEFACTOR = {"debug" : 4,
"release" : 1 }
suite_paths = utils.GetSuitePaths(join(workspace, "test"))
+ # Expand arguments with grouped tests. The args should reflect the list of
+ # suites as otherwise filters would break.
+ def ExpandTestGroups(name):
+ if name in TEST_MAP:
+ return [suite for suite in TEST_MAP[arg]]
+ else:
+ return [name]
+ args = reduce(lambda x, y: x + y,
+ [ExpandTestGroups(arg) for arg in args],
+ [])
+
if len(args) == 0:
suite_paths = [ s for s in DEFAULT_TESTS if s in suite_paths ]
else:
- args_suites = set()
+ args_suites = OrderedDict() # Used as set
for arg in args:
- suite = arg.split(os.path.sep)[0]
- if not suite in args_suites:
- args_suites.add(suite)
+ args_suites[arg.split(os.path.sep)[0]] = True
suite_paths = [ s for s in args_suites if s in suite_paths ]
suites = []
void ContextProviderInProcess::InitializeCapabilities() {
capabilities_.gpu = context3d_->GetImplementation()->capabilities();
+
+ size_t mapped_memory_limit = context3d_->GetMappedMemoryLimit();
+ capabilities_.max_transfer_buffer_usage_bytes =
+ mapped_memory_limit ==
+ WebGraphicsContext3DInProcessCommandBufferImpl::kNoLimit
+ ? std::numeric_limits<size_t>::max()
+ : mapped_memory_limit;
}
cc::ContextProvider::Capabilities
~WebGraphicsContext3DInProcessCommandBufferImpl() {
}
+size_t WebGraphicsContext3DInProcessCommandBufferImpl::GetMappedMemoryLimit() {
+ return context_->GetMappedMemoryLimit();
+}
+
bool WebGraphicsContext3DInProcessCommandBufferImpl::MaybeInitializeGL() {
if (initialized_)
return true;
// will need to be lost either when the first context requesting the
// discrete GPU is created, or the last one is destroyed.
gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
- context_.reset(GLInProcessContext::Create(NULL, /* service */
- NULL, /* surface */
- is_offscreen_,
- window_,
- gfx::Size(1, 1),
- NULL, /* share_context */
- share_resources_,
- attribs_,
- gpu_preference));
+ context_.reset(GLInProcessContext::Create(
+ NULL, /* service */
+ NULL, /* surface */
+ is_offscreen_,
+ window_,
+ gfx::Size(1, 1),
+ NULL, /* share_context */
+ share_resources_,
+ attribs_,
+ gpu_preference,
+ ::gpu::GLInProcessContextSharedMemoryLimits()));
}
if (context_) {
class WEBKIT_GPU_EXPORT WebGraphicsContext3DInProcessCommandBufferImpl
: public WebGraphicsContext3DImpl {
public:
+ enum MappedMemoryReclaimLimit {
+ kNoLimit = 0,
+ };
+
static scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>
CreateViewContext(
const blink::WebGraphicsContext3D::Attributes& attributes,
virtual ~WebGraphicsContext3DInProcessCommandBufferImpl();
+ size_t GetMappedMemoryLimit();
+
//----------------------------------------------------------------------
// WebGraphicsContext3D methods
virtual bool makeContextCurrent();
# Edit these when rolling DEPS.xwalk.
# -----------------------------------
-chromium_crosswalk_rev = '04ba13a65546e6e6309e560b9a2491b904ed57a8'
-blink_crosswalk_rev = '92e5d6adee53362b3f5aaec11bcb0526d5f0715d'
-v8_crosswalk_rev = '9b7376c845d7ba58715f4ffd9a80fd670b021360'
-ozone_wayland_rev = 'd301e5c546a7dea0de8fde5b07a2a57afd02103b'
+chromium_crosswalk_rev = '5ee6f9bf16ecbb3d56b195063b9b55d42effb67b'
+blink_crosswalk_rev = 'bc7b6c17bc9634579c6df664d04fdf38a1edd56a'
+v8_crosswalk_rev = '452135ceb9d31a6bc30fb39bf743623e0f553afa'
+ozone_wayland_rev = '9595d59e35e37675587523590e21397addf78446'
crosswalk_git = 'https://github.com/crosswalk-project'
ozone_wayland_git = 'https://github.com/01org'
-MAJOR=9
+MAJOR=10
MINOR=38
-BUILD=207
+BUILD=208
PATCH=0
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
+import android.util.Log;
import android.view.View;
import org.xwalk.app.runtime.extension.XWalkRuntimeExtensionManager;
private static final String DEFAULT_LIBRARY_APK_URL = null;
+ private static final String TAG = "XWalkRuntimeActivityBase";
+
private XWalkRuntimeView mRuntimeView;
private boolean mShownNotFoundDialog = false;
} else {
XWalkPreferences.setValue(XWalkPreferences.ANIMATABLE_XWALK_VIEW, false);
}
- mRuntimeView = new XWalkRuntimeView(this, this, null);
mShownNotFoundDialog = false;
if (mLibraryNotFoundDialog != null) mLibraryNotFoundDialog.cancel();
+ mRuntimeView = new XWalkRuntimeView(this, this, null);
if (mRemoteDebugging) {
XWalkPreferences.setValue(XWalkPreferences.REMOTE_DEBUGGING, true);
} else {
handleException(e.getCause());
return;
}
- throw new RuntimeException(e);
+ Log.e(TAG, Log.getStackTraceString(e));
}
private void showRuntimeLibraryExceptionDialog(String title, String message) {
#!/usr/bin/env python
-# Copyright (c) 2013 Intel Corporation. All rights reserved.
+# Copyright (c) 2013,2014 Intel Corporation. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import shutil
import stat
import sys
+import tempfile
# get xwalk absolute path so we can run this script from any location
xwalk_dir = os.path.dirname(os.path.abspath(__file__))
from handle_xml import EditElementAttribute
from handle_xml import EditElementValueByNodeName
from handle_permissions import HandlePermissions
+from util import CleanDir, CreateAndCopyDir
from xml.dom import minidom
TEMPLATE_DIR_NAME = 'template'
"""Copy the Android template project to a new app project
named app_info.app_name
"""
- # create new app_dir in xwalk_dir
+ # create new app_dir in temp dir
app_name = app_info.android_name
- app_dir = os.path.join(xwalk_dir, app_name)
+ app_dir = os.path.join(tempfile.gettempdir(), app_name)
app_package = app_info.package
app_root = app_info.app_root
template_app_dir = os.path.join(xwalk_dir, TEMPLATE_DIR_NAME)
# 1) copy template project to app_dir
- if os.path.exists(app_dir):
- shutil.rmtree(app_dir)
+ CleanDir(app_dir)
shutil.copytree(template_app_dir, app_dir)
# 2) replace app_dir 'src' dir with template 'src' dir
- shutil.rmtree(os.path.join(app_dir, 'src'))
+ CleanDir(os.path.join(app_dir, 'src'))
template_src_root = os.path.join(template_app_dir, 'src', 'org', 'xwalk',
'app', 'template')
# 3) Create directory tree from app package (org.xyz.foo -> src/org/xyz/foo)
- # and copy AppTemplateActivity.java to <app_name>Activity.java
+ # and copy AppTemplateActivity.java to <app_name>Activity.java
template_activity_file = os.path.join(template_src_root,
'AppTemplateActivity.java')
if not os.path.isfile(template_activity_file):
# 4) Copy all HTML source from app_root to app_dir
if app_root:
app_assets_dir = os.path.join(app_dir, 'assets', 'www')
- if os.path.isdir(app_assets_dir):
- shutil.rmtree(app_assets_dir)
+ CleanDir(app_assets_dir)
shutil.copytree(app_root, app_assets_dir)
if compressor:
CompressSourceFiles(app_assets_dir, compressor)
def CustomizeStringXML(name, description):
- strings_path = os.path.join(xwalk_dir, name, 'res', 'values', 'strings.xml')
+ strings_path = os.path.join(tempfile.gettempdir(), name, 'res', 'values',
+ 'strings.xml')
if not os.path.isfile(strings_path):
print ('Please make sure strings_xml'
' exists under template folder.')
def CustomizeThemeXML(name, fullscreen, manifest):
- theme_path = os.path.join(xwalk_dir, name, 'res', 'values-v14', 'theme.xml')
+ theme_path = os.path.join(tempfile.gettempdir(), name, 'res', 'values-v14',
+ 'theme.xml')
if not os.path.isfile(theme_path):
print('Error: theme.xml is missing in the build tool.')
sys.exit(6)
orientation = app_info.orientation
package = app_info.package
app_name = app_info.app_name
+ app_dir = os.path.join(tempfile.gettempdir(), name)
# Chinese character with unicode get from 'manifest.json' will cause
# 'UnicodeEncodeError' when finally wrote to 'AndroidManifest.xml'.
app_name = EncodingUnicodeValue(app_name)
# append a space before '@' or '?' to fix that.
if app_name.startswith('@') or app_name.startswith('?'):
app_name = ' ' + app_name
- manifest_path = os.path.join(xwalk_dir, name, 'AndroidManifest.xml')
+ manifest_path = os.path.join(app_dir, 'AndroidManifest.xml')
if not os.path.isfile(manifest_path):
print ('Please make sure AndroidManifest.xml'
' exists under template folder.')
EditElementAttribute(xmldoc, 'application', 'android:icon',
'@drawable/%s' % icon_name)
- file_handle = open(os.path.join(xwalk_dir, name, 'AndroidManifest.xml'), 'w')
+ file_handle = open(os.path.join(app_dir, 'AndroidManifest.xml'), 'w')
xmldoc.writexml(file_handle, encoding='utf-8')
file_handle.close()
def CustomizeJava(app_info, app_url, app_local_path, keep_screen_on):
name = app_info.android_name
package = app_info.package
- app_pkg_dir = os.path.join(xwalk_dir, name, 'src',
- package.replace('.', os.path.sep))
+ app_dir = os.path.join(tempfile.gettempdir(), name)
+ app_pkg_dir = os.path.join(app_dir, 'src', package.replace('.', os.path.sep))
dest_activity = os.path.join(app_pkg_dir, name + 'Activity.java')
ReplaceString(dest_activity, 'org.xwalk.app.template', package)
ReplaceString(dest_activity, 'AppTemplate', name)
- manifest_file = os.path.join(xwalk_dir, name, 'assets/www', 'manifest.json')
+ manifest_file = os.path.join(app_dir, 'assets', 'www', 'manifest.json')
if os.path.isfile(manifest_file):
ReplaceString(
dest_activity,
ReplaceString(dest_activity, 'file:///android_asset/www/index.html',
app_url)
elif app_local_path:
- if os.path.isfile(os.path.join(xwalk_dir, name, 'assets/www',
- app_local_path)):
+ if os.path.isfile(os.path.join(app_dir, 'assets', 'www', app_local_path)):
ReplaceString(dest_activity, 'file:///android_asset/www/index.html',
'app://' + package + '/' + app_local_path)
else:
if not extensions:
return
name = app_info.android_name
- apk_path = os.path.join(xwalk_dir, name)
- apk_assets_path = os.path.join(apk_path, 'assets')
+ app_dir = os.path.join(tempfile.gettempdir(), name)
+ apk_assets_path = os.path.join(app_dir, 'assets')
extensions_string = 'xwalk-extensions'
# Set up the target directories and files.
- dest_jar_path = os.path.join(apk_path, extensions_string)
+ dest_jar_path = os.path.join(app_dir, extensions_string)
os.mkdir(dest_jar_path)
dest_js_path = os.path.join(apk_assets_path, extensions_string)
os.mkdir(dest_js_path)
json_output['jsapi'] = js_path_prefix + json_output['jsapi']
extension_json_list.append(json_output)
# Merge the permissions of extensions into AndroidManifest.xml.
- manifest_path = os.path.join(xwalk_dir, name, 'AndroidManifest.xml')
+ manifest_path = os.path.join(app_dir, 'AndroidManifest.xml')
xmldoc = minidom.parse(manifest_path)
if ('permissions' in json_output):
# Get used permission list to avoid repetition as "--permissions"
def GenerateCommandLineFile(app_info, xwalk_command_line):
if xwalk_command_line == '':
return
- assets_path = os.path.join(xwalk_dir, app_info.android_name, 'assets')
+ assets_path = os.path.join(tempfile.gettempdir(), app_info.android_name,
+ 'assets')
file_path = os.path.join(assets_path, 'xwalk-command-line')
command_line_file = open(file_path, 'w')
command_line_file.write('xwalk ' + xwalk_command_line)
def CustomizeIconByDict(name, app_root, icon_dict):
+ app_dir = os.path.join(tempfile.gettempdir(), name)
icon_name = None
drawable_dict = {'ldpi': [1, 37], 'mdpi': [37, 72], 'hdpi': [72, 96],
'xhdpi': [96, 120], 'xxhdpi': [120, 144],
for kd, vd in drawable_dict.items():
for item in icon_list:
if item[0] >= vd[0] and item[0] < vd[1]:
- drawable_path = os.path.join(xwalk_dir, name, 'res', 'drawable-' + kd)
+ drawable_path = os.path.join(app_dir, 'res', 'drawable-' + kd)
if not os.path.exists(drawable_path):
os.makedirs(drawable_path)
icon = os.path.join(app_root, item[1])
def CustomizeIconByOption(name, icon):
if os.path.isfile(icon):
- drawable_path = os.path.join(xwalk_dir, name, 'res', 'drawable')
+ drawable_path = os.path.join(tempfile.gettempdir(), name, 'res', 'drawable')
if not os.path.exists(drawable_path):
os.makedirs(drawable_path)
icon_file = os.path.basename(icon)
icon_name = os.path.splitext(icon_file)[0]
return icon_name
else:
- print('Error: "%s" does not exist.')
+ print('Error: "%s" does not exist.' % icon)
sys.exit(6)
'Crosswalk is powered by Chromium and supports Chromium command line.'
'For example, '
'--xwalk-command-line=\'--chromium-command-1 --xwalk-command-2\'')
+ info = ('Create an Android project directory at this location. ')
+ parser.add_option('--project-dir', help=info)
parser.add_option('--xwalk-command-line', default='', help=info)
info = ('Minify and obfuscate javascript and css.'
'--compressor: compress javascript and css.'
options.permissions, options.app_url, options.app_local_path,
options.keep_screen_on, options.extensions, None,
options.xwalk_command_line, options.compressor)
+
+ # build project is now in /tmp/<name>. Copy to project_dir
+ if options.project_dir:
+ src_dir = os.path.join(tempfile.gettempdir(), app_info.android_name)
+ dest_dir = os.path.join(options.project_dir, app_info.android_name)
+ CreateAndCopyDir(src_dir, dest_dir, True)
+
except SystemExit as ec:
print('Exiting with error code: %d' % ec.code)
return ec.code
+ finally:
+ CleanDir(os.path.join(tempfile.gettempdir(), app_info.android_name))
return 0
import os
import shutil
import sys
-
-xwalk_dir = os.path.dirname(os.path.abspath(__file__))
+import tempfile
def CopyToPathWithName(root, name, final_path, rename):
if name == '':
def CopyDrawables(image_dict, orientation, sanitized_name, name, app_root):
- drawable = os.path.join(xwalk_dir, sanitized_name, 'res', 'drawable')
+ drawable = os.path.join(tempfile.gettempdir(), sanitized_name, 'res',
+ 'drawable')
if orientation == 'landscape':
drawable = drawable + '-land'
elif orientation == 'portrait':
orientation,
sanitized_name,
app_root):
- background_path = os.path.join(xwalk_dir, sanitized_name, 'res',
+ background_path = os.path.join(tempfile.gettempdir(), sanitized_name, 'res',
'drawable', 'launchscreen_bg.xml')
if not os.path.isfile(background_path):
print('Error: launchscreen_bg.xml is missing in the build tool.')
import shutil
import subprocess
import sys
+import tempfile
# get xwalk absolute path so we can run this script from any location
xwalk_dir = os.path.dirname(os.path.abspath(__file__))
ParseParameterForCompressor
from extension_manager import GetExtensionList, GetExtensionStatus
from handle_permissions import permission_mapping_table
-from util import AllArchitectures, CleanDir, GetVersion, RunCommand
+from util import AllArchitectures, CleanDir, GetVersion, RunCommand, \
+ CreateAndCopyDir
from manifest_json_parser import HandlePermissionList
from manifest_json_parser import ManifestJsonParser
def ParseXPK(options, out_dir):
- cmd = ['python', os.path.join (xwalk_dir, 'parse_xpk.py'),
+ cmd = ['python', os.path.join(xwalk_dir, 'parse_xpk.py'),
'--file=%s' % os.path.expanduser(options.xpk),
'--out=%s' % out_dir]
RunCommand(cmd)
extension_binary_path_list = GetExtensionBinaryPathList()
if len(extension_binary_path_list) > 0:
if options.extensions is None:
- options.extensions = ""
+ options.extensions = ""
else:
options.extensions += os.pathsep
def Execution(options, name):
+ app_dir = os.path.join(tempfile.gettempdir(), name)
android_path = Which('android')
if android_path is None:
print('The "android" binary could not be found. Check your Android SDK '
# Update android project for app and xwalk_core_library.
update_project_cmd = [android_path, 'update', 'project',
- '--path', os.path.join (xwalk_dir, name),
+ '--path', app_dir,
'--target', target_string,
'--name', name]
if options.mode == 'embedded':
RunCommand([android_path, 'update', 'lib-project',
- '--path', os.path.join(xwalk_dir, name, 'xwalk_core_library'),
+ '--path', os.path.join(app_dir, 'xwalk_core_library'),
'--target', target_string])
update_project_cmd.extend(['-l', 'xwalk_core_library'])
- else:
- # Shared mode doesn't need xwalk_runtime_java.jar.
- os.remove(os.path.join(xwalk_dir, name, 'libs', 'xwalk_runtime_java.jar'))
RunCommand(update_project_cmd)
# Check whether external extensions are included.
extensions_string = 'xwalk-extensions'
- extensions_dir = os.path.join(xwalk_dir, name, extensions_string)
+ extensions_dir = os.path.join(app_dir, extensions_string)
external_extension_jars = FindExtensionJars(extensions_dir)
for external_extension_jar in external_extension_jars:
shutil.copyfile(external_extension_jar,
- os.path.join(xwalk_dir, name, 'libs',
+ os.path.join(app_dir, 'libs',
os.path.basename(external_extension_jar)))
if options.mode == 'embedded':
if not arch:
print ('Invalid CPU arch: %s.' % arch)
sys.exit(10)
- library_lib_path = os.path.join(xwalk_dir, name, 'xwalk_core_library',
- 'libs')
+ library_lib_path = os.path.join(app_dir, 'xwalk_core_library', 'libs')
for dir_name in os.listdir(library_lib_path):
lib_dir = os.path.join(library_lib_path, dir_name)
if ContainsNativeLibrary(lib_dir):
shutil.rmtree(lib_dir)
- native_lib_path = os.path.join(xwalk_dir, name, 'native_libs', arch)
+ native_lib_path = os.path.join(app_dir, 'native_libs', arch)
if ContainsNativeLibrary(native_lib_path):
shutil.copytree(native_lib_path, os.path.join(library_lib_path, arch))
else:
'embedded APK.' % arch)
sys.exit(10)
- ant_cmd = [ant_path, 'release', '-f',
- os.path.join(xwalk_dir, name, 'build.xml')]
+ if options.project_only:
+ return
+
+ # Build the APK
+ ant_cmd = [ant_path, 'release', '-f', os.path.join(app_dir, 'build.xml')]
if not options.verbose:
ant_cmd.extend(['-quiet'])
ant_cmd.extend(['-Dkey.store=%s' % os.path.abspath(key_store)])
% (' '.join(ant_cmd), ant_result))
sys.exit(ant_result)
- src_file = os.path.join(xwalk_dir, name, 'bin', '%s-release.apk' % name)
+ src_file = os.path.join(app_dir, 'bin', '%s-release.apk' % name)
package_name = name
if options.app_version:
package_name += ('_' + options.app_version)
def MakeApk(options, app_info, manifest):
Customize(options, app_info, manifest)
name = app_info.android_name
+ app_dir = os.path.join(tempfile.gettempdir(), name)
packaged_archs = []
if options.mode == 'shared':
+ # For shared mode, it's not necessary to use the whole xwalk core library,
+ # use xwalk_core_library_java_app_part.jar from it is enough.
+ java_app_part_jar = os.path.join(xwalk_dir, 'xwalk_core_library', 'libs',
+ 'xwalk_core_library_java_app_part.jar')
+ shutil.copy(java_app_part_jar, os.path.join(app_dir, 'libs'))
Execution(options, name)
elif options.mode == 'embedded':
# Copy xwalk_core_library into app folder and move the native libraries
# out.
# When making apk for specified CPU arch, will only include the
# corresponding native library by copying it back into xwalk_core_library.
- target_library_path = os.path.join(xwalk_dir, name, 'xwalk_core_library')
+ target_library_path = os.path.join(app_dir, 'xwalk_core_library')
shutil.copytree(os.path.join(xwalk_dir, 'xwalk_core_library'),
target_library_path)
library_lib_path = os.path.join(target_library_path, 'libs')
- native_lib_path = os.path.join(xwalk_dir, name, 'native_libs')
+ native_lib_path = os.path.join(app_dir, 'native_libs')
os.makedirs(native_lib_path)
available_archs = []
for dir_name in os.listdir(library_lib_path):
print('No packages created, aborting')
sys.exit(13)
- PrintPackageInfo(options, name, packaged_archs)
+ # if project_dir, save build directory
+ if options.project_dir:
+ save_dir = os.path.join(options.project_dir, name)
+ if CreateAndCopyDir(app_dir, save_dir, True):
+ print ('\nA project directory was created successfully in %s' %
+ save_dir)
+ print ('To generate an APK manually, go to %s and run the '
+ 'following command:' % save_dir)
+ print (' ant release -f build.xml')
+ print ('For more information, see\n'
+ ' http://developer.android.com/tools/building/'
+ 'building-cmdline.html')
+ else:
+ print ('Error: Unable to create a project directory during the build. '
+ 'Please check the directory passed in --project-dir, '
+ 'available disk space, and write permission.')
+
+ if not options.project_only:
+ PrintPackageInfo(options, name, packaged_archs)
def main(argv):
parser = optparse.OptionParser()
'For example, --app-local-path=/relative/path/of/entry/file')
group.add_option('--app-local-path', help=info)
parser.add_option_group(group)
+ # Mandatory options group
group = optparse.OptionGroup(parser, 'Mandatory arguments',
'They are used for describing the APK information through '
'command line options.')
'--package=com.example.YourPackage')
group.add_option('--package', help=info)
parser.add_option_group(group)
+ # Optional options group (alphabetical)
group = optparse.OptionGroup(parser, 'Optional arguments',
'They are used for various settings for applications through '
'command line options.')
'be made by adding a prefix based on architecture to the version '
'code base. For example, --app-versionCodeBase=24')
group.add_option('--app-versionCodeBase', type='int', help=info)
- info = ('Use command lines.'
- 'Crosswalk is powered by Chromium and supports Chromium command line.'
- 'For example, '
- '--xwalk-command-line=\'--chromium-command-1 --xwalk-command-2\'')
- group.add_option('--xwalk-command-line', default='', help=info)
info = ('The description of the application. For example, '
'--description=YourApplicationDescription')
group.add_option('--description', help=info)
info = ('The list of permissions to be used by web application. For example, '
'--permissions=geolocation:webgl')
group.add_option('--permissions', help=info)
- info = ('Packaging tool will move the output APKS to the target directory')
+ info = ('Create an Android project directory with Crosswalk at this location.'
+ ' (See project-only option below)')
+ group.add_option('--project-dir', help=info)
+ info = ('Must be used with project-dir option. Create an Android project '
+ 'directory with Crosswalk but do not build the APK package')
+ group.add_option('--project-only', action='store_true', default=False,
+ dest='project_only', help=info)
+ info = ('Packaging tool will move the output APKs to the target directory')
group.add_option('--target-dir', default=os.getcwd(), help=info)
+ info = ('Use command lines.'
+ 'Crosswalk is powered by Chromium and supports Chromium command line.'
+ 'For example, '
+ '--xwalk-command-line=\'--chromium-command-1 --xwalk-command-2\'')
+ group.add_option('--xwalk-command-line', default='', help=info)
parser.add_option_group(group)
+ # Keystore options group
group = optparse.OptionGroup(parser, 'Keystore Options',
'The keystore is a signature from web developer, it\'s used when '
'developer wants to distribute the applications.')
xpk_temp_dir = ''
if options.xpk:
xpk_name = os.path.splitext(os.path.basename(options.xpk))[0]
- xpk_temp_dir = os.path.join(xwalk_dir, xpk_name + '_xpk')
+ xpk_temp_dir = os.path.join(tempfile.gettempdir(), xpk_name + '_xpk')
+ CleanDir(xpk_temp_dir)
ParseXPK(options, xpk_temp_dir)
if options.app_root and not options.manifest:
if not os.path.isdir(target_dir):
os.makedirs(target_dir)
+ if options.project_dir:
+ if options.project_dir == tempfile.gettempdir():
+ print('\nmake_apk.py error: Option --project-dir can not be '
+ 'the system temporary\ndirectory.')
+ sys.exit(8)
+ if options.project_only and not options.project_dir:
+ print('\nmake_apk.py error: Option --project-only must be used '
+ 'with --project-dir')
+ sys.exit(8)
+
try:
MakeApk(options, app_info, manifest)
except SystemExit as ec:
- CleanDir(app_info.android_name)
- CleanDir('out')
- CleanDir(xpk_temp_dir)
return ec.code
+ finally:
+ CleanDir(os.path.join(tempfile.gettempdir(), app_info.android_name))
+ CleanDir(xpk_temp_dir)
return 0
if options.mode == 'embedded':
native_library_dir = os.path.join('xwalk_core_library', 'libs')
native_library_temp_dir = 'temp'
+ Clean('temp', '0') # May be left over from aborted tests
shutil.copytree(native_library_dir, native_library_temp_dir)
for root, _, files in os.walk(native_library_dir):
if 'libxwalkcore.so' in files:
cmd = ['python', 'make_apk.py', '--name=Example',
'--package=org.xwalk.example', '--app-version=1.0.0',
'--description=a sample application',
- '--app-url=http://www.intel.com', self._mode]
+ '--app-url=http://www.intel.com',
+ '--project-dir=.', self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
manifest = 'Example/AndroidManifest.xml'
'--package=org.xwalk.example', '--app-version=1.0.0',
'--description=a sample application',
'--app-versionCode=3',
- '--app-url=http://www.intel.com', self._mode]
+ '--app-url=http://www.intel.com',
+ '--project-dir=.',
+ self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
manifest = 'Example/AndroidManifest.xml'
'--description=a sample application',
'--app-versionCodeBase=3',
arch,
- '--app-url=http://www.intel.com', self._mode]
+ '--app-url=http://www.intel.com',
+ '--project-dir=.',
+ self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
manifest = 'Example/AndroidManifest.xml'
'--description=a sample application',
'--app-versionCodeBase=30000000',
arch,
- '--app-url=http://www.intel.com', self._mode]
+ '--app-url=http://www.intel.com',
+ '--project-dir=.',
+ self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
manifest = 'Example/AndroidManifest.xml'
def testPermissions(self):
cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0',
'--package=org.xwalk.example', '--permissions=geolocation',
- '--app-url=http://www.intel.com', self._mode]
+ '--app-url=http://www.intel.com',
+ '--project-dir=.',
+ self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
manifest = 'Example/AndroidManifest.xml'
icon = os.path.join('test_data', 'manifest', 'icons', 'icon_96.png')
cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0',
'--package=org.xwalk.example', '--app-url=http://www.intel.com',
- '--icon=%s' % icon, self._mode]
+ '--icon=%s' % icon, '--project-dir=.', self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
manifest = 'Example/AndroidManifest.xml'
manifest_path = os.path.join('test_data', 'manifest', 'manifest_icon.json')
cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0',
'--package=org.xwalk.example', '--app-url=http://www.intel.com',
- '--manifest=%s' % manifest_path, self._mode]
+ '--manifest=%s' % manifest_path, '--project-dir=.', self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
manifest = 'Example/AndroidManifest.xml'
def testFullscreen(self):
cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0',
'--package=org.xwalk.example', '--app-url=http://www.intel.com',
- '-f', self._mode]
+ '-f', '--project-dir=.', self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
theme = 'Example/res/values-v14/theme.xml'
def testEnableRemoteDebugging(self):
cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0',
'--package=org.xwalk.example', '--app-url=http://www.intel.com',
- '--enable-remote-debugging', self._mode]
+ '--enable-remote-debugging', '--project-dir=.', self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
activity = 'Example/src/org/xwalk/example/ExampleActivity.java'
manifest_path = os.path.join('test_data', 'manifest', 'manifest.json')
cmd = ['python', 'make_apk.py', '--enable-remote-debugging',
'--package=org.xwalk.example',
- '--manifest=%s' % manifest_path, self._mode]
+ '--manifest=%s' % manifest_path,
+ '--project-dir=.',
+ self._mode]
RunCommand(cmd)
activity = 'Example/src/org/xwalk/example/ExampleActivity.java'
with open(activity, 'r') as content_file:
'--package=org.xwalk.example', '--app-url=http://www.intel.com',
'--keystore-path=%s' % keystore_path, '--keystore-alias=xwalk-test',
'--keystore-passcode=xwalk-test',
- '--keystore-alias-passcode=xwalk-test', self._mode]
+ '--keystore-alias-passcode=xwalk-test',
+ '--project-dir=.', self._mode]
RunCommand(cmd)
self.assertTrue(os.path.isdir('Example'))
self.checkApks('Example', '1.0.0', keystore_path)
'--keystore-path=%s' % keystore_path_with_space,
'--keystore-alias=xwalk test',
'--keystore-passcode=xwalk-test',
- '--keystore-alias-passcode=xwalk test', self._mode]
+ '--keystore-alias-passcode=xwalk test',
+ '--project-dir=.', self._mode]
RunCommand(cmd)
self.assertTrue(os.path.isdir('Example'))
self.checkApks('Example', '1.0.0', keystore_path_with_space)
def testManifest(self):
manifest_path = os.path.join('test_data', 'manifest', 'manifest.json')
cmd = ['python', 'make_apk.py', '--package=org.xwalk.example',
- '--manifest=%s' % manifest_path, self._mode]
+ '--manifest=%s' % manifest_path, '--project-dir=.', self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
manifest = 'Example/AndroidManifest.xml'
manifest_path = os.path.join('test_data', 'manifest',
'manifest_app_launch_local_path.json')
cmd = ['python', 'make_apk.py', '--package=org.xwalk.example',
- '--manifest=%s' % manifest_path, self._mode]
+ '--manifest=%s' % manifest_path, '--project-dir=.', self._mode]
out = RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
self.assertTrue(out.find('no app launch path') == -1)
extension_path = 'test_data/extensions/myextension'
cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0',
'--package=org.xwalk.example', '--app-url=http://www.intel.com',
- '--extensions=%s' % extension_path, self._mode]
+ '--extensions=%s' % extension_path, '--project-dir=.', self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
self.assertTrue(os.path.exists('Example'))
cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0',
'--package=org.xwalk.example', '--app-root=%s' % test_entry_root,
'--app-local-path=contactextension.html',
- '--extensions=%s' % extension_path, self._mode]
+ '--extensions=%s' % extension_path,
+ '--project-dir=.', self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
self.assertTrue(os.path.exists('Example'))
def testXPK(self):
xpk_file = os.path.join('test_data', 'xpk', 'example.xpk')
cmd = ['python', 'make_apk.py', '--package=org.xwalk.example',
- '--xpk=%s' % xpk_file, self._mode]
+ '--xpk=%s' % xpk_file, '--project-dir=.', self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
self.assertTrue(os.path.exists('Example'))
def testXPKWithError(self):
xpk_file = os.path.join('test_data', 'xpk', 'error.xpk')
cmd = ['python', 'make_apk.py', '--package=org.xwalk.example',
- '--xpk=%s' % xpk_file, self._mode]
+ '--xpk=%s' % xpk_file, '--project-dir=.', self._mode]
out = RunCommand(cmd)
error_msg = 'XPK doesn\'t contain manifest file'
self.assertTrue(out.find(error_msg) != -1)
def testOrientation(self):
cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0',
'--package=org.xwalk.example', '--app-url=http://www.intel.com',
- '--orientation=landscape', self._mode]
+ '--orientation=landscape', '--project-dir=.', self._mode]
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
manifest = 'Example/AndroidManifest.xml'
def testVerbose(self):
cmd = ['python', 'make_apk.py', '--name=Example', '--app-version=1.0.0',
'--package=org.xwalk.example', '--app-url=http://www.intel.com',
- '--verbose', self._mode]
+ '--verbose', '--project-dir=.', self._mode]
result = RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
self.assertTrue(result.find('aapt') != -1)
'--name=Example',
'--orientation=landscape',
'--package=org.xwalk.example',
- '--permissions=geolocation']
+ '--permissions=geolocation',
+ '--project-dir=.']
RunCommand(cmd)
self.addCleanup(Clean, 'Example', '1.0.0')
activity = 'Example/src/org/xwalk/example/ExampleActivity.java'
'--app-version=1.0.0',
'--name=Example',
'--package=org.xwalk.example',
- '--verbose']
+ '--verbose',
+ '--project-dir=.']
RunCommand(cmd)
manifest = 'Example/AndroidManifest.xml'
if not os.path.exists(manifest):
Clean('Example', '1.0.0')
manifest_path = os.path.join('test_data', 'launchScreen', 'manifest.json')
cmd = ['python', 'make_apk.py', '--package=org.xwalk.example',
- '--manifest=%s' % manifest_path, self._mode]
+ '--manifest=%s' % manifest_path, '--project-dir=.', self._mode]
RunCommand(cmd)
# Check theme.xml.
theme_path = os.path.join('Example', 'res', 'values-v14', 'theme.xml')
'--name=%s' % name,
'--package=org.xwalk.example',
'--compressor',
- '--app-root=%s' % app_root]
+ '--app-root=%s' % app_root,
+ '--project-dir=.']
RunCommand(cmd)
CompareSizeForCompressor('all', css_file, 'css', name, fun)
CompareSizeForCompressor('all', js_file, 'js', name, fun)
'--name=%s' % name,
'--package=org.xwalk.example',
'--app-root=%s' % app_root,
- '--compressor']
+ '--compressor',
+ '--project-dir=.']
RunCommand(cmd)
CompareSizeForCompressor('all', css_file, 'css', name, fun)
CompareSizeForCompressor('all', js_file, 'js', name, fun)
'--name=%s' % name,
'--package=org.xwalk.example',
'--compressor=js',
- '--app-root=%s' % app_root]
+ '--app-root=%s' % app_root,
+ '--project-dir=.']
RunCommand(cmd)
CompareSizeForCompressor('js', js_file, 'js', name, fun)
'--name=%s' % name,
'--package=org.xwalk.example',
'--compressor=css',
- '--app-root=%s' % app_root]
+ '--app-root=%s' % app_root,
+ '--project-dir=.']
RunCommand(cmd)
CompareSizeForCompressor('css', css_file, 'css', name, fun)
cmd = ['python', 'customize.py',
'--name=%s' % name,
'--package=org.xwalk.example',
- '--app-root=%s' % app_root]
+ '--app-root=%s' % app_root,
+ '--project-dir=.']
RunCommand(cmd)
CompareSizeForCompressor(None, css_file, 'css', name, fun)
CompareSizeForCompressor(None, js_file, 'js', name, fun)
'--name=%s' % name,
'--package=org.xwalk.example',
'--app-root=%s' % app_root,
- '--compressor=other']
+ '--compressor=other',
+ '--project-dir=.']
RunCommand(cmd)
CompareSizeForCompressor(None, css_file, 'css', name, fun)
CompareSizeForCompressor(None, js_file, 'js', name, fun)
xml_path = 'Example/AndroidManifest.xml'
piece_content = 'android:label="%s"' % '你好'
cmd = ['python', 'make_apk.py', '--name=你好', '--app-version=1.0.0',
- '--package=org.xwalk.example', '--app-url=http://www.intel.com']
+ '--package=org.xwalk.example', '--app-url=http://www.intel.com',
+ '--project-dir=.']
RunCommand(cmd)
self.VerifyResultInXMLFile(xml_path, piece_content)
manifest_path = os.path.join('test_data', 'manifest', 'invalidchars',
'manifest_with_chinese_name.json')
cmd = ['python', 'make_apk.py', '--package=org.xwalk.example',
- '--manifest=%s' % manifest_path]
+ '--manifest=%s' % manifest_path, '--project-dir=.']
RunCommand(cmd)
self.VerifyResultInXMLFile(xml_path, piece_content)
piece_content = '<string name="description">%s</string>' % '你好'
cmd = ['python', 'make_apk.py', '--name=hello', '--app-version=1.0.0',
'--package=org.xwalk.example', '--app-url=http://www.intel.com',
- '--description=你好']
+ '--description=你好', '--project-dir=.']
RunCommand(cmd)
self.VerifyResultInXMLFile(xml_path, piece_content)
manifest_path = os.path.join('test_data', 'manifest',
'manifest_description_dbcs.json')
cmd = ['python', 'make_apk.py', '--package=org.xwalk.example',
- '--manifest=%s' % manifest_path]
+ '--manifest=%s' % manifest_path, '--project-dir=.']
RunCommand(cmd)
piece_content = '"description">%s</string>' % '你好 a sample description'
self.VerifyResultInXMLFile(xml_path, piece_content)
version_str += ('.').join(version_nums)
file_handle.close()
return version_str
+
+
+def CreateAndCopyDir(src_dir, dest_dir, delete_if_exists=False):
+ if not os.path.isdir(src_dir):
+ return False
+ # create path, except last directory (handled by copytree)
+ pre_dest_dir = os.path.dirname(dest_dir)
+ if not os.path.isdir(pre_dest_dir):
+ try:
+ os.makedirs(pre_dest_dir) # throws exception on error
+ except OSError:
+ return False
+ if os.path.exists(dest_dir):
+ if delete_if_exists:
+ shutil.rmtree(dest_dir)
+ else:
+ return False
+ shutil.copytree(src_dir, dest_dir)
+ return True
+
scoped_refptr<xwalk::application::ApplicationData> data) {
base::ThreadRestrictions::SetIOAllowed(true);
base::FileEnumerator iter(
- data->Path(), true,
+ data->path(), true,
base::FileEnumerator::FILES,
FILE_PATH_LITERAL("index.*"));
size_t priority = arraysize(kDefaultWidgetEntryPage);
template<>
GURL Application::GetStartURL<Manifest::TYPE_WIDGET>() {
- GURL url = GetAbsoluteURLFromKey(widget_keys::kLaunchLocalPathKey);
- if (!url.is_valid()) {
- LOG(WARNING) << "Failed to find start URL from the 'config.xml'"
- << "trying to find default entry page.";
- url = GetDefaultWidgetEntryPage(data_);
- }
-
- if (url.is_valid()) {
#if defined(OS_TIZEN)
- if (data_->IsHostedApp() && !url.SchemeIsHTTPOrHTTPS()) {
- LOG(ERROR) << "Hosted apps are only supported with"
- "http:// or https:// scheme.";
- return GURL();
- }
+ if (data_->IsHostedApp()) {
+ std::string source;
+ data_->GetManifest()->GetString(widget_keys::kLaunchLocalPathKey, &source);
+ GURL url = GURL(source);
+
+ if (url.is_valid() && url.SchemeIsHTTPOrHTTPS())
+ return url;
+ }
#endif
+
+ GURL url = GetAbsoluteURLFromKey(widget_keys::kLaunchLocalPathKey);
+ if (url.is_valid())
+ return url;
+
+ LOG(WARNING) << "Failed to find start URL from the 'config.xml'"
+ << "trying to find default entry page.";
+ url = GetDefaultWidgetEntryPage(data_);
+ if (url.is_valid())
return url;
- }
LOG(WARNING) << "Failed to find a valid start URL in the manifest.";
return GURL();
template<>
GURL Application::GetStartURL<Manifest::TYPE_MANIFEST>() {
+ if (data_->IsHostedApp()) {
+ std::string source;
+ data_->GetManifest()->GetString(keys::kStartURLKey, &source);
+ // Not trying to get a relative path for the "fake" application.
+ return GURL(source);
+ }
+
GURL url = GetAbsoluteURLFromKey(keys::kStartURLKey);
if (url.is_valid())
return url;
if (!manifest->GetString(key, &source) || source.empty())
return GURL();
- std::size_t found = source.find("://");
- if (found == std::string::npos)
- return data_->GetResourceURL(source);
- return GURL(source);
+ return data_->GetResourceURL(source);
}
void Application::Terminate() {
base::FilePath directory_path;
std::string content_security_policy;
if (application) {
- directory_path = application->Path();
+ directory_path = application->path();
const char* csp_key = GetCSPKey(application->manifest_type());
const CSPInfo* csp_info = static_cast<CSPInfo*>(
if (app_data->source_type() == ApplicationData::TEMP_DIRECTORY) {
LOG(INFO) << "Deleting the app temporary directory "
- << app_data->Path().AsUTF8Unsafe();
+ << app_data->path().AsUTF8Unsafe();
content::BrowserThread::PostTask(content::BrowserThread::FILE,
FROM_HERE, base::Bind(base::IgnoreResult(&base::DeleteFile),
- app_data->Path(), true /*recursive*/));
+ app_data->path(), true /*recursive*/));
// FIXME: So far we simply clean up all the app persistent data,
// further we need to add an appropriate logic to handle it.
content::BrowserContext::GarbageCollectStoragePartitions(
namespace application {
+blink::WebScreenOrientationLockType GetDefaultOrientation(
+ const base::WeakPtr<Application>& app) {
+ TizenSettingInfo* info = static_cast<TizenSettingInfo*>(
+ app->data()->GetManifestData(widget_keys::kTizenSettingKey));
+ if (!info)
+ return blink::WebScreenOrientationLockDefault;
+ switch (info->screen_orientation()) {
+ case TizenSettingInfo::PORTRAIT:
+ return blink::WebScreenOrientationLockPortrait;
+ case TizenSettingInfo::LANDSCAPE:
+ return blink::WebScreenOrientationLockLandscape;
+ case TizenSettingInfo::AUTO:
+ return blink::WebScreenOrientationLockAny;
+ default:
+ NOTREACHED();
+ return blink::WebScreenOrientationLockDefault;
+ }
+}
+
class ScreenOrientationProviderTizen :
public content::ScreenOrientationProvider {
public:
}
virtual void UnlockOrientation() OVERRIDE {
- LockOrientation(request_id_, blink::WebScreenOrientationLockDefault);
+ LockOrientation(request_id_, GetDefaultOrientation(app_));
}
virtual void OnOrientationChange() OVERRIDE {}
std::set<Runtime*>::iterator it = runtimes_.begin();
for (; it != runtimes_.end(); ++it) {
if ((*it)->window())
- (*it)->window()->Hide();
+ (*it)->window()->Minimize();
}
}
bool ApplicationTizen::Launch(const LaunchParams& launch_params) {
if (Application::Launch(launch_params)) {
DCHECK(web_contents_);
- web_contents_->GetScreenOrientationDispatcherHost()->
- SetProvider(new ScreenOrientationProviderTizen(GetWeakPtr()));
+ content::ScreenOrientationProvider *provider =
+ new ScreenOrientationProviderTizen(GetWeakPtr());
+ web_contents_->GetScreenOrientationDispatcherHost()->SetProvider(provider);
+
+ provider->LockOrientation(0, GetDefaultOrientation(GetWeakPtr()));
return true;
}
return false;
base::FilePath ApplicationTizen::GetSplashScreenPath() {
if (TizenSplashScreenInfo* ss_info = static_cast<TizenSplashScreenInfo*>(
data()->GetManifestData(widget_keys::kTizenSplashScreenKey))) {
- return data()->Path().Append(FILE_PATH_LITERAL(ss_info->src()));
+ return data()->path().Append(FILE_PATH_LITERAL(ss_info->src()));
}
return base::FilePath();
}
return ret_val;
}
+GURL ApplicationData::GetResourceURL(const std::string& relative_path) const {
+ if (!base::PathExists(path_.Append(relative_path))) {
+ LOG(ERROR) << "The path does not exist in the application directory: "
+ << relative_path;
+ return GURL();
+ }
+
+ return GetResourceURL(URL(), relative_path);
+}
+
bool ApplicationData::Init(const std::string& explicit_id,
base::string16* error) {
DCHECK(error);
// NOTE: Static so that it can be used from multiple threads.
static GURL GetResourceURL(const GURL& application_url,
const std::string& relative_path);
- GURL GetResourceURL(const std::string& relative_path) const {
- return GetResourceURL(URL(), relative_path);
- }
+ GURL GetResourceURL(const std::string& relative_path) const;
// Returns the base application url for a given |application_id|.
static GURL GetBaseURLFromApplicationId(const std::string& application_id);
void SetManifestData(const std::string& key, ManifestData* data);
// Accessors:
-
- const base::FilePath& Path() const { return path_; }
- void SetPath(const base::FilePath& path) { path_ = path; }
+ const base::FilePath& path() const { return path_; }
+#if defined(OS_TIZEN) // FIXME : This method should be removed.
+ void set_path(const base::FilePath& path) { path_ = path; }
+#endif
const GURL& URL() const { return application_url_; }
SourceType source_type() const { return source_type_; }
Manifest::Type manifest_type() const { return manifest_->type(); }
const char kTizenSplashScreenKey[] = "widget.splash-screen";
const char kTizenSplashScreenSrcKey[] = "@src";
const char kContentNamespace[] = "widget.content.@namespace";
+const char kTizenScreenOrientationKey[] = "widget.setting.@screen-orientation";
#endif
} // namespace application_widget_keys
extern const char kTizenSplashScreenKey[];
extern const char kTizenSplashScreenSrcKey[];
extern const char kContentNamespace[];
+ extern const char kTizenScreenOrientationKey[];
#endif
} // namespace application_widget_keys
#include <map>
#include <utility>
+#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "xwalk/application/common/application_manifest_constants.h"
namespace application {
TizenSettingInfo::TizenSettingInfo()
- : hwkey_enabled_(true) {}
+ : hwkey_enabled_(true),
+ screen_orientation_(PORTRAIT) {}
TizenSettingInfo::~TizenSettingInfo() {}
manifest->GetString(keys::kTizenHardwareKey, &hwkey);
app_info->set_hwkey_enabled(hwkey != "disable");
+ std::string screen_orientation;
+ manifest->GetString(keys::kTizenScreenOrientationKey, &screen_orientation);
+ if (base::strcasecmp("portrait", screen_orientation.c_str()) == 0)
+ app_info->set_screen_orientation(TizenSettingInfo::PORTRAIT);
+ else if (base::strcasecmp("landscape", screen_orientation.c_str()) == 0)
+ app_info->set_screen_orientation(TizenSettingInfo::LANDSCAPE);
+ else
+ app_info->set_screen_orientation(TizenSettingInfo::AUTO);
+
application->SetManifestData(keys::kTizenSettingKey,
app_info.release());
return true;
" or not specified in configuration file.");
return false;
}
+
+ std::string screen_orientation;
+ manifest->GetString(keys::kTizenScreenOrientationKey, &screen_orientation);
+ if (!screen_orientation.empty() &&
+ base::strcasecmp("portrait", screen_orientation.c_str()) != 0 &&
+ base::strcasecmp("landscape", screen_orientation.c_str()) != 0 &&
+ base::strcasecmp("auto-rotation", screen_orientation.c_str()) != 0) {
+ *error = std::string("The screen-orientation must be 'portrait'/"
+ "'landscape'/'auto-rotation' or not specified.");
+ return false;
+ }
return true;
}
TizenSettingInfo();
virtual ~TizenSettingInfo();
+ enum ScreenOrientation {
+ PORTRAIT,
+ LANDSCAPE,
+ AUTO
+ };
+
void set_hwkey_enabled(bool enabled) { hwkey_enabled_ = enabled; }
bool hwkey_enabled() const { return hwkey_enabled_; }
+ void set_screen_orientation(ScreenOrientation orientation) {
+ screen_orientation_ = orientation;
+ }
+
+ ScreenOrientation screen_orientation() const { return screen_orientation_; }
+
private:
bool hwkey_enabled_;
+ ScreenOrientation screen_orientation_;
};
class TizenSettingHandler : public ManifestHandler {
splash_screen->GetAsDictionary(&ss_dict);
std::string ss_src;
ss_dict->GetString(keys::kTizenSplashScreenSrcKey, &ss_src);
- base::FilePath path = application->Path().Append(FILE_PATH_LITERAL(ss_src));
+ base::FilePath path = application->path().Append(FILE_PATH_LITERAL(ss_src));
if (!base::PathExists(path)) {
*error = std::string("The splash screen image does not exist");
return false;
#include "third_party/re2/re2/re2.h"
#include "xwalk/application/common/tizen/signature_data.h"
#include "xwalk/application/common/tizen/signature_parser.h"
+#include "xwalk/application/common/tizen/signature_xmlsec_adaptor.h"
namespace {
SignatureFileSet::reverse_iterator iter = signature_set.rbegin();
bool ret = false;
for (; iter != signature_set.rend(); ++iter) {
- LOG(INFO) << "Checking signature with id=" << iter->file_number();
// Verify whether signature xml is a valid [XMLDSIG] document.
if (!XMLSchemaValidate(*iter, widget_path)) {
LOG(ERROR) << "Validating " << iter->file_name() << "schema failed.";
return INVALID;
// Perform reference validation and signature validation on signature
- // TODO(XU): depends on root CA certificate installed on Tizen platform.
+ if (!SignatureXmlSecAdaptor::ValidateFile(*data.get(), widget_path))
+ return INVALID;
}
return VALID;
}
--- /dev/null
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Copyright (C) 2002-2003 Aleksey Sanin. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "xwalk/application/common/tizen/signature_xmlsec_adaptor.h"
+
+#include <list>
+#include <map>
+#include <string>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "net/cert/x509_certificate.h"
+#include "libxml/parser.h"
+#include "xmlsec/crypto.h"
+#include "xmlsec/io.h"
+#include "xmlsec/keysmngr.h"
+#include "xmlsec/xmlsec.h"
+#include "xmlsec/xmltree.h"
+#include "xmlsec/xmldsig.h"
+#ifndef XMLSEC_NO_XSLT
+#include "libxslt/xslt.h"
+#endif // XMLSEC_NO_XSLT
+
+namespace {
+
+// TODO(XU): Once tizen platform provide certificate manager util APIs,
+// we should call API from system to query certificate's file path.
+class CertificateUtil {
+ public:
+ static const std::map<std::string, std::string>& certificate_path() {
+ return certificate_path_;
+ }
+
+ private:
+ static std::map<std::string, std::string> InitCertificatePath() {
+ std::map<std::string, std::string> root_certificates;
+ root_certificates["Tizen Partner-Manufacturer Distributor Root CA"] =
+ "tizen-distributor-root-ca-partner-manufacturer.pem";
+ root_certificates["SLP WebApp Temporary CA"] =
+ "tizen.root.preproduction.cert.pem";
+ root_certificates["Tizen Test Developer Root CA"] =
+ "tizen-developer-root-ca.pem";
+ root_certificates["Tizen Developers Root"] =
+ "tizen-developers-root.pem";
+ root_certificates["Tizen Partner Distributor Root CA"] =
+ "tizen-distributor-root-ca-partner.pem";
+ root_certificates["Tizen Partner-Operator Distributor Root CA"] =
+ "tizen-distributor-root-ca-partner-operator.pem";
+ root_certificates["Tizen Public Distributor Root CA"] =
+ "tizen-distributor-root-ca-public.pem";
+ root_certificates["Partner Class Developer Root"] =
+ "tizen-partner-class-developer-root.pem";
+ root_certificates["Partner Class Root Authority"] =
+ "tizen-partner-class-root-authority.pem";
+ root_certificates["Platform Class Developer Root"] =
+ "tizen-platform-class-developer-root.pem";
+ root_certificates["Platform Class Root Authority"] =
+ "tizen-platform-class-root-authority.pem";
+ root_certificates["Public Class Developer Root"] =
+ "tizen-public-class-developer-root.pem";
+ root_certificates["Public Class Root Authority"] =
+ "tizen-public-class-root-authority.pem";
+
+ return root_certificates;
+ }
+
+ static std::map<std::string, std::string> certificate_path_;
+};
+
+std::map<std::string, std::string>
+ CertificateUtil::certificate_path_ = InitCertificatePath();
+
+class XmlSecContext {
+ public:
+ static void GetExtractedPath(const xwalk::application::SignatureData& data);
+ static xmlSecKeysMngrPtr LoadTrustedCerts(
+ const xwalk::application::SignatureData& signature_data);
+ static int VerifyFile(
+ xmlSecKeysMngrPtr mngr, const xwalk::application::SignatureData& data);
+
+ private:
+ static int FileMatchCallback(const char* file_name);
+ static void* FileOpenCallback(const char* file_name);
+ static int FileReadCallback(void* context, char* buffer, int len);
+ static int FileCloseCallback(void* context);
+ static void ConvertToPemCert(std::string* cert);
+ static base::FilePath GetCertFromStore(const std::string& subject);
+
+ static std::string prefix_path_;
+ static std::pair<void*, bool> file_wrapper_;
+};
+
+std::string XmlSecContext::prefix_path_;
+std::pair<void*, bool> XmlSecContext::file_wrapper_;
+
+void XmlSecContext::GetExtractedPath(
+ const xwalk::application::SignatureData& data) {
+ XmlSecContext::prefix_path_ = data.GetExtractedWidgetPath().MaybeAsASCII();
+}
+
+int XmlSecContext::FileMatchCallback(const char* file_name) {
+ std::string path = XmlSecContext::prefix_path_ + std::string(file_name);
+ return xmlFileMatch(path.c_str());
+}
+
+void* XmlSecContext::FileOpenCallback(const char* file_name) {
+ std::string path = XmlSecContext::prefix_path_ + std::string(file_name);
+ XmlSecContext::file_wrapper_ =
+ std::make_pair(xmlFileOpen(path.c_str()), false);
+ return &(XmlSecContext::file_wrapper_);
+}
+
+int XmlSecContext::FileReadCallback(void* context, char* buffer, int len) {
+ std::pair<void*, bool>* file_wrapper =
+ static_cast<std::pair<void*, bool>*>(context);
+ DCHECK(file_wrapper);
+ if (file_wrapper->second)
+ return 0;
+
+ int output = xmlFileRead(file_wrapper->first, buffer, len);
+ if (output == 0) {
+ file_wrapper->second = true;
+ xmlFileClose(file_wrapper->first);
+ }
+ return output;
+}
+
+int XmlSecContext::FileCloseCallback(void* context) {
+ std::pair<void*, bool>* file_wrapper =
+ static_cast<std::pair<void*, bool>*>(context);
+ DCHECK(file_wrapper);
+ int output = 0;
+ if (!file_wrapper->second)
+ output = xmlFileClose(file_wrapper->first);
+
+ return output;
+}
+
+xmlSecKeysMngrPtr XmlSecContext::LoadTrustedCerts(
+ const xwalk::application::SignatureData& signature_data) {
+ xmlSecKeysMngrPtr mngr = xmlSecKeysMngrCreate();
+ if (!mngr) {
+ LOG(ERROR) << "Error: failed to create keys manager.";
+ return NULL;
+ }
+ if (xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
+ LOG(ERROR) << "Error: failed to initialize keys manager.";
+ xmlSecKeysMngrDestroy(mngr);
+ return NULL;
+ }
+
+ std::list<std::string> certificate_list = signature_data.certificate_list();
+ std::string cert;
+ std::string issuer;
+ for (std::list<std::string>::iterator it = certificate_list.begin();
+ it != certificate_list.end(); ++it) {
+ cert = *it;
+ XmlSecContext::ConvertToPemCert(&cert);
+ net::CertificateList certs =
+ net::X509Certificate::CreateCertificateListFromBytes(
+ cert.data(), cert.length(), net::X509Certificate::FORMAT_AUTO);
+ issuer = certs[0]->issuer().GetDisplayName();
+
+ if (xmlSecCryptoAppKeysMngrCertLoadMemory(mngr,
+ reinterpret_cast<const unsigned char*>(cert.c_str()), cert.size(),
+ xmlSecKeyDataFormatCertPem, xmlSecKeyDataTypeTrusted) < 0) {
+ LOG(ERROR) << "Error: failed to load pem certificate.";
+ xmlSecKeysMngrDestroy(mngr);
+ return NULL;
+ }
+ }
+
+ const base::FilePath& root_cert_path =
+ XmlSecContext::GetCertFromStore(issuer);
+ if (!base::PathExists(root_cert_path)) {
+ LOG(ERROR) << "Failed to find root certificate.";
+ return NULL;
+ }
+
+ if (xmlSecCryptoAppKeysMngrCertLoad(mngr,
+ root_cert_path.MaybeAsASCII().c_str(), xmlSecKeyDataFormatPem,
+ xmlSecKeyDataTypeTrusted) < 0) {
+ LOG(ERROR) << "Error: failed to load root certificate";
+ xmlSecKeysMngrDestroy(mngr);
+ return NULL;
+ }
+
+ return mngr;
+}
+
+// Verifies XML signature in #xml_file
+// Returns 0 on success or a negative value if an error occurs.
+int XmlSecContext::VerifyFile(xmlSecKeysMngrPtr mngr,
+ const xwalk::application::SignatureData& data) {
+ LOG(INFO) << "Verify " << data.signature_file_name();
+ xmlSecIOCleanupCallbacks();
+ XmlSecContext::GetExtractedPath(data);
+ xmlSecIORegisterCallbacks(
+ XmlSecContext::FileMatchCallback,
+ XmlSecContext::FileOpenCallback,
+ XmlSecContext::FileReadCallback,
+ XmlSecContext::FileCloseCallback);
+
+ xmlDocPtr doc = xmlParseFile(data.signature_file_name().c_str());
+ if (!doc) {
+ LOG(ERROR) << "Error: failed to parse " << data.signature_file_name();
+ return -1;
+ }
+
+ if (!xmlDocGetRootElement(doc)) {
+ LOG(ERROR) << "Error: unable to get root element.";
+ xmlFreeDoc(doc);
+ return -1;
+ }
+
+ xmlNodePtr node = xmlSecFindNode(
+ xmlDocGetRootElement(doc), xmlSecNodeSignature, xmlSecDSigNs);
+ if (!node) {
+ LOG(ERROR) << "Error: unable to find SecNodeSignature node.";
+ xmlFreeDoc(doc);
+ return -1;
+ }
+
+ xmlSecDSigCtxPtr dsig_ctx = xmlSecDSigCtxCreate(mngr);
+ if (!dsig_ctx) {
+ LOG(ERROR) << "Error: failed to create signature context.";
+ xmlFreeDoc(doc);
+ return -1;
+ }
+
+ if (xmlSecDSigCtxVerify(dsig_ctx, node) < 0) {
+ LOG(ERROR) << "Error: signature verify.";
+ xmlFreeDoc(doc);
+ xmlSecDSigCtxDestroy(dsig_ctx);
+ return -1;
+ }
+
+ int res = -1;
+ if (dsig_ctx->status != xmlSecDSigStatusSucceeded)
+ LOG(ERROR) << "Signature " << data.signature_file_name() <<" is INVALID";
+
+ LOG(INFO) << "Signature "<< data.signature_file_name() << " is OK.";
+ res = 0;
+
+ xmlFreeDoc(doc);
+ xmlSecDSigCtxDestroy(dsig_ctx);
+ return res;
+}
+
+void XmlSecContext::ConvertToPemCert(std::string* cert) {
+ *cert = "-----BEGIN CERTIFICATE-----" + *cert;
+ *cert = *cert + "-----END CERTIFICATE-----";
+}
+
+base::FilePath XmlSecContext::GetCertFromStore(const std::string& subject) {
+ const char cert_prefix_path[] = "/usr/share/ca-certificates/tizen/";
+ std::map<std::string, std::string>::const_iterator iter =
+ CertificateUtil::certificate_path().find(subject);
+
+ if (iter == CertificateUtil::certificate_path().end()) {
+ LOG(ERROR) << "Failing to find root certificate.";
+ return base::FilePath("");
+ }
+ LOG(INFO) << "root cert path is " << cert_prefix_path + iter->second;
+ return base::FilePath(cert_prefix_path + iter->second);
+}
+
+} // namespace
+
+namespace xwalk {
+namespace application {
+
+// static
+bool SignatureXmlSecAdaptor::ValidateFile(
+ const SignatureData& signature_data, const base::FilePath& widget_path) {
+ xmlInitParser();
+ xmlSubstituteEntitiesDefault(1);
+#ifndef XMLSEC_NO_XSLT
+ xsltSecurityPrefsPtr xslt_sec_prefs = xsltNewSecurityPrefs();
+ xsltSetSecurityPrefs(
+ xslt_sec_prefs, XSLT_SECPREF_READ_FILE, xsltSecurityForbid);
+ xsltSetSecurityPrefs(
+ xslt_sec_prefs, XSLT_SECPREF_WRITE_FILE, xsltSecurityForbid);
+ xsltSetSecurityPrefs(
+ xslt_sec_prefs, XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid);
+ xsltSetSecurityPrefs(
+ xslt_sec_prefs, XSLT_SECPREF_READ_NETWORK, xsltSecurityForbid);
+ xsltSetSecurityPrefs(
+ xslt_sec_prefs, XSLT_SECPREF_WRITE_NETWORK, xsltSecurityForbid);
+ xsltSetDefaultSecurityPrefs(xslt_sec_prefs);
+#endif // XMLSEC_NO_XSLT
+
+ if (xmlSecInit() < 0) {
+ LOG(ERROR) << "Error: xmlsec initialization failed.";
+ return false;
+ }
+
+ if (xmlSecCheckVersion() != 1) {
+ LOG(ERROR) << "Error: loaded xmlsec library version is not compatible.";
+ return false;
+ }
+
+#ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
+ if (xmlSecCryptoDLLoadLibrary(BAD_CAST XMLSEC_CRYPTO) < 0) {
+ LOG(ERROR) << "Error: unable to load default xmlsec-crypto library.";
+ return false;
+ }
+#endif // XMLSEC_CRYPTO_DYNAMIC_LOADING
+
+ if (xmlSecCryptoAppInit(NULL) < 0) {
+ LOG(ERROR) << "Error: crypto initialization failed.";
+ return false;
+ }
+
+ if (xmlSecCryptoInit() < 0) {
+ LOG(ERROR) << "Error: xmlsec-crypto initialization failed.";
+ return false;
+ }
+
+ xmlSecKeysMngrPtr mngr = XmlSecContext::LoadTrustedCerts(signature_data);
+ if (!mngr)
+ return false;
+
+ if (XmlSecContext::VerifyFile(mngr, signature_data) < 0) {
+ xmlSecKeysMngrDestroy(mngr);
+ return false;
+ }
+
+ xmlSecKeysMngrDestroy(mngr);
+ xmlSecCryptoShutdown();
+ xmlSecCryptoAppShutdown();
+ xmlSecShutdown();
+
+#ifndef XMLSEC_NO_XSLT
+ xsltFreeSecurityPrefs(xslt_sec_prefs);
+ xsltCleanupGlobals();
+#endif // XMLSEC_NO_XSLT
+ xmlCleanupParser();
+
+ return true;
+}
+
+} // namespace application
+} // namespace xwalk
--- /dev/null
+// Copyright (c) 2014 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef XWALK_APPLICATION_COMMON_TIZEN_SIGNATURE_XMLSEC_ADAPTOR_H_
+#define XWALK_APPLICATION_COMMON_TIZEN_SIGNATURE_XMLSEC_ADAPTOR_H_
+
+#include "base/files/file_path.h"
+#include "xwalk/application/common/tizen/signature_data.h"
+
+namespace xwalk {
+namespace application {
+
+class SignatureXmlSecAdaptor {
+ public:
+ static bool ValidateFile(const SignatureData& signature_data,
+ const base::FilePath& widget_path);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SignatureXmlSecAdaptor);
+};
+
+} // namespace application
+} // namespace xwalk
+
+#endif // XWALK_APPLICATION_COMMON_TIZEN_SIGNATURE_XMLSEC_ADAPTOR_H_
'../../build/system.gyp:tizen',
'../../tizen/xwalk_tizen.gypi:xwalk_tizen_lib',
'../../../third_party/re2/re2.gyp:re2',
+ '../../../net/net.gyp:net',
],
+ 'cflags': [
+ '<!@(pkg-config --cflags xmlsec1)',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '<!@(pkg-config --libs-only-l xmlsec1)',
+ ],
+ },
'sources': [
'manifest_handlers/navigation_handler.cc',
'manifest_handlers/navigation_handler.h',
'tizen/signature_parser.cc',
'tizen/signature_validator.cc',
'tizen/signature_validator.h',
+ 'tizen/signature_xmlsec_adaptor.cc',
+ 'tizen/signature_xmlsec_adaptor.h',
],
}],
],
using xwalk::application::Manifest;
using xwalk::application::GetManifestPath;
-class ApplicationMultiAppTest : public ApplicationBrowserTest {
+class ApplicationTest : public ApplicationBrowserTest {
};
-IN_PROC_BROWSER_TEST_F(ApplicationMultiAppTest, TestMultiApp) {
+IN_PROC_BROWSER_TEST_F(ApplicationTest, TestMultiApp) {
ApplicationService* service = application_sevice();
const size_t currently_running_count = service->active_applications().size();
// Launch the first app.
base::FilePath manifest_path =
GetManifestPath(test_data_dir_.Append(FILE_PATH_LITERAL("dummy_app1")),
Manifest::TYPE_MANIFEST);
- Application* app1 = application_sevice()->LaunchFromManifestPath(
+ Application* app1 = service->LaunchFromManifestPath(
manifest_path, Manifest::TYPE_MANIFEST);
ASSERT_TRUE(app1);
// Wait for app is fully loaded.
// Verify that no new App instance was created, if one exists
// with the same ID.
- Application* failed_app1 = application_sevice()->LaunchFromManifestPath(
+ Application* failed_app1 = service->LaunchFromManifestPath(
manifest_path, Manifest::TYPE_MANIFEST);
ASSERT_FALSE(failed_app1);
content::RunAllPendingInMessageLoop();
EXPECT_EQ(service->active_applications().size(), currently_running_count);
}
+
+IN_PROC_BROWSER_TEST_F(ApplicationTest, TestUnsafeStartURL) {
+ base::FilePath manifest_path = GetManifestPath(
+ test_data_dir_.Append(FILE_PATH_LITERAL("unsafe_start_URL")),
+ Manifest::TYPE_MANIFEST);
+ Application* app = application_sevice()->LaunchFromManifestPath(
+ manifest_path, Manifest::TYPE_MANIFEST);
+ EXPECT_EQ(NULL, app);
+}
--- /dev/null
+{
+ "name": "unsafe",
+ "start_url": "http://unsafe.com"
+}
// Whole app directory size in KB
int64 CountAppTotalSize(
scoped_refptr<xwalk::application::ApplicationData> app_data) {
- return base::ComputeDirectorySize(app_data->Path()) / 1024;
+ return base::ComputeDirectorySize(app_data->path()) / 1024;
}
// Data directory size in KB
scoped_refptr<xwalk::application::ApplicationData> app_data) {
int64 size = 0;
- base::FilePath private_path = app_data->Path().Append("private");
+ base::FilePath private_path = app_data->path().Append("private");
size += base::ComputeDirectorySize(private_path);
- base::FilePath tmp_path = app_data->Path().Append("tmp");
+ base::FilePath tmp_path = app_data->path().Append("tmp");
size += base::ComputeDirectorySize(tmp_path);
return size / 1024;
return false;
}
- app_data->SetPath(app_dir);
+ app_data->set_path(app_dir);
if (!storage_->AddApplication(app_data)) {
LOG(ERROR) << "Application with id " << app_data->ID()
return false;
}
- const base::FilePath& app_dir = old_app_data->Path();
+ const base::FilePath& app_dir = old_app_data->path();
const base::FilePath tmp_dir(app_dir.value()
+ FILE_PATH_LITERAL(".tmp"));
(os.path.join(source_code_dir, 'xwalk/app/android/app_template'),
app_target_dir),
- # Shared mode uses xwalk_app_runtime_java.jar only.
- # Embedded mode needs both.
(os.path.join(jar_src_dir, 'xwalk_app_runtime_java.jar'), jar_target_dir),
- (os.path.join(jar_src_dir, 'xwalk_runtime_java.jar'), jar_target_dir),
# XWalk Core Library
(xwalk_core_library_dir, os.path.join(target_dir, 'xwalk_core_library')),
os.mkdir(libs_dir)
libs_to_copy = [
- 'xwalk_core_library_java.jar',
+ 'xwalk_core_library_java_app_part.jar',
+ 'xwalk_core_library_java_library_part.jar',
]
for lib in libs_to_copy:
'R.txt'),
(os.path.join(options.target, 'xwalk_core_library', 'AndroidManifest.xml'),
'AndroidManifest.xml'),
- (os.path.join(options.target, 'xwalk_core_library', 'libs',
- 'xwalk_core_library_java.jar'),
+ (os.path.join(options.target, 'lib.java', 'xwalk_core_library_java.jar'),
'classes.jar'),
)
# This is a list of files that will not be packaged: mostly a blacklist of
# files within |dirs|.
exclude_files = (
os.path.join(options.target, 'xwalk_core_library', 'libs',
- 'xwalk_core_library_java.jar'),
+ 'xwalk_core_library_java_app_part.jar'),
+ os.path.join(options.target, 'xwalk_core_library', 'libs',
+ 'xwalk_core_library_java_library_part.jar'),
)
aar_path = os.path.join(options.target, 'xwalk_core_library.aar')
%endif
Name: crosswalk
-Version: 9.38.207.0
+Version: 10.38.208.0
Release: 0
Summary: Chromium-based app runtime
License: (BSD-3-Clause and LGPL-2.1+)
BuildRequires: pkgconfig(nss)
BuildRequires: pkgconfig(sensor)
BuildRequires: pkgconfig(vconf)
+BuildRequires: pkgconfig(xmlsec1)
%if %{with x}
BuildRequires: pkgconfig(x11)
BuildRequires: pkgconfig(xcomposite)
%else
BuildRequires: pkgconfig(scim)
%endif
+Requires: ca-certificates-tizen
%description
Crosswalk is an app runtime based on Chromium. It is an open source project started by the Intel Open Source Technology Center (http://www.01.org).
Constructor<?> loadConstructor() {
Class<?> clazz = loadClass(fullClassName);
+ if (clazz == null) return null;
Class<?>[] params = new Class<?>[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
Object type = paramTypes[i];
ResourceExtractor.setMandatoryPaksToExtract(MANDATORY_PAKS);
final int resourcesListResId = context.getResources().getIdentifier(
XWALK_RESOURCES_LIST_RES_NAME, "array", context.getPackageName());
- if (resourcesListResId != 0) {
+ final AssetManager assets = context.getAssets();
+ if (!context.getPackageName().equals(context.getApplicationContext().getPackageName()) ||
+ resourcesListResId != 0) {
+ // For shared mode, assets are in library package.
+ // For embedding API usage, assets are in res/raw.
ResourceExtractor.setResourceIntercepter(new ResourceIntercepter() {
@Override
public Set<String> getInterceptableResourceList() {
- try {
- Set<String> resourcesList = new HashSet<String>();
- String[] resources = context.getResources().getStringArray(resourcesListResId);
- for (String resource : resources) {
- resourcesList.add(resource);
+ Set<String> resourcesList = new HashSet<String>();
+ if (!context.getPackageName().equals(
+ context.getApplicationContext().getPackageName())) {
+ try {
+ for (String resource : assets.list("")) {
+ resourcesList.add(resource);
+ }
+ } catch (IOException e){}
+ }
+ if (resourcesListResId != 0) {
+ try {
+ String[] resources = context.getResources().getStringArray(resourcesListResId);
+ for (String resource : resources) {
+ resourcesList.add(resource);
+ }
+ } catch (NotFoundException e) {
+ Log.w(TAG, "R.array." + XWALK_RESOURCES_LIST_RES_NAME + " can't be found.");
}
- return resourcesList;
- } catch (NotFoundException e) {
- Log.w(TAG, "R.array." + XWALK_RESOURCES_LIST_RES_NAME + " can't be found.");
}
- return null;
+ return resourcesList;
}
@Override
public InputStream interceptLoadingForResource(String resource) {
- String resourceName = resource.split("\\.")[0];
- int resId = context.getResources().getIdentifier(
- resourceName, "raw", context.getPackageName());
- try {
- if (resId != 0) return context.getResources().openRawResource(resId);
- } catch (NotFoundException e) {
- Log.w(TAG, "R.raw." + resourceName + " can't be found.");
+ if (!context.getPackageName().equals(
+ context.getApplicationContext().getPackageName())) {
+ try {
+ InputStream fromAsset = context.getAssets().open(resource);
+ if (fromAsset != null) return fromAsset;
+ } catch (IOException e) {
+ Log.w(TAG, resource + " can't be found in assets.");
+ }
}
+
+ if (resourcesListResId != 0) {
+ String resourceName = resource.split("\\.")[0];
+ int resId = context.getResources().getIdentifier(
+ resourceName, "raw", context.getPackageName());
+ try {
+ if (resId != 0) return context.getResources().openRawResource(resId);
+ } catch (NotFoundException e) {
+ Log.w(TAG, "R.raw." + resourceName + " can't be found.");
+ }
+ }
+
return null;
}
});
// TODO(yongsheng): make it static?
@XWalkAPI
public String getAPIVersion() {
- return "2.1";
+ return "3.0";
}
/**
}
private void updatePresentationView(Display preferredDisplay) {
+ Activity activity = mActivity.get();
+ if (activity == null) return;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 ||
preferredDisplay == null) {
return;
ViewGroup parent = (ViewGroup)mPresentationContent.getContentView().getParent();
if (parent != null) parent.removeView(mPresentationContent.getContentView());
- mPresentationView = PresentationView.createInstance(mContext, preferredDisplay);
+ mPresentationView = PresentationView.createInstance(activity, preferredDisplay);
mPresentationView.setContentView(mPresentationContent.getContentView());
mPresentationView.setPresentationListener(new PresentationView.PresentationListener() {
@Override
void XWalkBrowserMainParts::PreMainMessageLoopStart() {
CommandLine* command_line = CommandLine::ForCurrentProcess();
command_line->AppendSwitch(switches::kEnableViewport);
- command_line->AppendSwitch(switches::kEnableViewportMeta);
command_line->AppendSwitch(xswitches::kEnableOverlayScrollbars);
command_line->AppendSwitch(
switches::kDisableOverlayFullscreenVideoSubtitle);
+ command_line->AppendSwitch(switches::kEnableViewportMeta);
+
XWalkBrowserMainParts::PreMainMessageLoopStart();
startup_url_ = GURL();
{
'variables': {
- 'core_library_empty_embedder_apk_name': 'XWalkCoreLibraryEmptyEmbedder',
+ 'core_internal_empty_embedder_apk_name': 'XWalkCoreInternalEmptyEmbedder',
},
'targets': [
{
],
},
{
- 'target_name': 'xwalk_core_library_empty_embedder_apk',
+ 'target_name': 'xwalk_core_internal_empty_embedder_apk',
'type': 'none',
'dependencies': [
'libxwalkcore',
'xwalk_core_internal_java',
- 'xwalk_core_java',
],
'variables': {
- 'apk_name': '<(core_library_empty_embedder_apk_name)',
- 'java_in_dir': 'runtime/android/core_library_empty',
+ 'apk_name': '<(core_internal_empty_embedder_apk_name)',
+ 'java_in_dir': 'runtime/android/core_internal_empty',
'native_lib_target': 'libxwalkcore',
'is_test_apk': 1,
'additional_src_dirs': [
},
},
{
- 'target_name': 'xwalk_core_library_java',
+ 'target_name': 'xwalk_core_library_java_app_part',
'type': 'none',
'dependencies': [
- 'xwalk_core_internal_java',
'xwalk_core_java',
- 'xwalk_core_library_empty_embedder_apk',
],
'variables': {
'classes_dir': '<(PRODUCT_DIR)/<(_target_name)/classes',
'message': 'Creating <(_target_name) jar',
'inputs': [
'build/android/merge_jars.py',
+ '>@(input_jars_paths)',
+ ],
+ 'outputs': [
+ '<(jar_final_path)',
+ ],
+ 'action': [
+ 'python', 'build/android/merge_jars.py',
+ '--classes-dir=<(classes_dir)',
+ '--jars=>(input_jars_paths)',
+ '--jar-path=<(jar_final_path)',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'xwalk_core_library_java_library_part',
+ 'type': 'none',
+ 'dependencies': [
+ 'xwalk_core_internal_empty_embedder_apk',
+ ],
+ 'variables': {
+ 'classes_dir': '<(PRODUCT_DIR)/<(_target_name)/classes',
+ 'jar_name': '<(_target_name).jar',
+ 'jar_final_path': '<(PRODUCT_DIR)/lib.java/<(jar_name)',
+ },
+ 'actions': [
+ {
+ 'action_name': 'jars_<(_target_name)',
+ 'message': 'Creating <(_target_name) jar',
+ 'inputs': [
+ 'build/android/merge_jars.py',
+ '>@(input_jars_paths)',
],
'outputs': [
- '<(PRODUCT_DIR)/pack_xwalk_core_library_java_intermediate/always_run',
+ '<(jar_final_path)',
+ ],
+ 'action': [
+ 'python', 'build/android/merge_jars.py',
+ '--classes-dir=<(classes_dir)',
+ '--jars=>(input_jars_paths)',
+ '--jar-path=<(jar_final_path)',
+ ],
+ },
+ ],
+ },
+ {
+ 'target_name': 'xwalk_core_library_java',
+ 'type': 'none',
+ 'dependencies': [
+ 'xwalk_core_library_java_app_part',
+ 'xwalk_core_library_java_library_part',
+ ],
+ 'variables': {
+ 'classes_dir': '<(PRODUCT_DIR)/<(_target_name)/classes',
+ 'jar_name': '<(_target_name).jar',
+ 'jar_final_path': '<(PRODUCT_DIR)/lib.java/<(jar_name)',
+ },
+ 'actions': [
+ {
+ 'action_name': 'jars_<(_target_name)',
+ 'message': 'Creating <(_target_name) jar',
+ 'inputs': [
+ 'build/android/merge_jars.py',
+ '>@(input_jars_paths)',
+ ],
+ 'outputs': [
+ '<(jar_final_path)',
],
'action': [
'python', 'build/android/merge_jars.py',
'type': 'none',
'dependencies': [
'xwalk_core_shell_apk',
- 'xwalk_core_library_java',
+ 'xwalk_core_library_java_app_part',
+ 'xwalk_core_library_java_library_part',
],
'actions': [
{
'type': 'none',
'dependencies': [
'xwalk_core_library',
+ 'xwalk_core_library_java',
],
'actions': [
{
'sources': [
'application/test/application_browsertest.cc',
'application/test/application_browsertest.h',
- 'application/test/application_multi_app_test.cc',
+ 'application/test/application_test.cc',
'application/test/application_testapi.cc',
'application/test/application_testapi.h',
'application/test/application_testapi_test.cc',