%endif
Name: crosswalk
-Version: 9.37.195.0
+Version: 9.37.197.0
Release: 0
Summary: Crosswalk is an app runtime based on Chromium
License: (BSD-3-Clause and LGPL-2.1+)
GYP_EXTRA_FLAGS="${GYP_EXTRA_FLAGS} -Ddisable_fatal_linker_warnings=1"
%endif
-# Temporarily disable Alsa support while snd_seq_* support is not enabled on
-# Tizen. See https://codereview.chromium.org/264973012 and
-# https://review.tizen.org/gerrit/#/c/24336/
-GYP_EXTRA_FLAGS="${GYP_EXTRA_FLAGS} -Duse_alsa=0"
-
# Temporarily disable WebRTC support because its build currently hardcodes
# dependencies on X11 and OpenSSL. We are still trying to get some
# clarifications as to whether this is really necessary. See XWALK-2160.
'git_url':
'https://chromium.googlesource.com',
'webkit_rev':
- '@052a22b0345eccf119172f8ee6bae904d04fe172',
+ '@85b8bad59be0f106c1123b12043fc14d9a0034de',
'angle_revision':
- '75551cf9a7c65bcdb9f9fe44ac78ca15da37942e'
+ 'f4126d2b9eb008d4e792fff2592f338e19c68569'
}
deps = {
'src/tools/swarming_client':
Var('git_url') + '/external/swarming.client.git@c333224ef8cf5cc41ab91f6ee9cb1fdbe3689f19',
'src/v8':
- Var('git_url') + '/external/v8.git@644e49bdbb46608c3292d19df9145c0793927e54',
+ Var('git_url') + '/external/v8.git@14b092be52bd160007e9b57fa6692f5093d071c4',
}
deps_os = {
'src/third_party/WebKit/LayoutTests/w3c/web-platform-tests': None,\r
'src/chrome/test/data/extensions/api_test/permissions/nacl_enabled/bin': None,\r
'src/third_party/WebKit/public':\r
- (Var("blink")) + '/branches/chromium/2062/public@180122',\r
+ (Var("blink")) + '/branches/chromium/2062/public@180557',\r
'src/native_client': None,\r
'src/tools/page_cycler/acid3': None,\r
'src/testing/iossim/third_party/class-dump':\r
'src/third_party/libwebm/source':\r
(Var("git.chromium.org")) + '/webm/libwebm.git@8be63972fdd7ae8c041778f8ba674ade903395c9',\r
'src/third_party/WebKit':\r
- (Var("blink")) + '/branches/chromium/2062@180122',\r
+ (Var("blink")) + '/branches/chromium/2062@180557',\r
'src/third_party/openmax_dl':\r
(Var("webrtc")) + '/deps/third_party/openmax@6483',\r
'src/third_party/libc++abi/trunk':\r
'src/sdch/open-vcdiff':\r
(Var("open-vcdiff")) + '/trunk@42',\r
'src/third_party/angle':\r
- (Var("git.chromium.org")) + '/angle/angle.git@75551cf9a7c65bcdb9f9fe44ac78ca15da37942e',\r
+ (Var("git.chromium.org")) + '/angle/angle.git@f4126d2b9eb008d4e792fff2592f338e19c68569',\r
'build/third_party/lighttpd':\r
'/trunk/deps/third_party/lighttpd@58968',\r
'src/buildtools':\r
'src/third_party/libsrtp':\r
'/trunk/deps/third_party/libsrtp@261337',\r
'src/v8':\r
- (Var("v8")) + '/branches/3.27@22914',\r
+ (Var("v8")) + '/branches/3.27@23158',\r
'src/third_party/pywebsocket/src':\r
(Var("pywebsocket")) + '/trunk/src@790',\r
'src/third_party/libvpx':\r
-# Please include joth@ and (joi@ or erikwright@) on the review for any changes
+# Please include benm@ and (erikwright@) on the review for any changes
# to DEPS files under android_webview/
# Do not add any includes under chrome/ anywhere in android_webview.
"-android_webview/lib",
"+components/data_reduction_proxy",
+ "+content/browser/media/android",
"+content/public/common",
"+gpu",
"+jni",
'../components/components.gyp:visitedlink_renderer',
'../components/components.gyp:web_contents_delegate_android',
'../content/content.gyp:content_app_both',
+ '../content/content.gyp:content_browser',
'../gpu/gpu.gyp:command_buffer_service',
'../gpu/gpu.gyp:gles2_implementation',
'../gpu/gpu.gyp:gl_in_process_context',
'includes': [ '../build/java.gypi' ],
},
],
- }, { # android_webview_build==1
+ }, { # android_webview_build==1
'targets': [
{
'target_name': 'android_webview_jarjar_ui_resources',
'browser/net/input_stream_reader_unittest.cc',
'lib/main/webview_tests.cc',
'native/aw_contents_client_bridge_unittest.cc',
+ 'native/aw_media_url_interceptor_unittest.cc',
'native/input_stream_unittest.cc',
'native/permission/media_access_permission_request_unittest.cc',
'native/permission/permission_request_handler_unittest.cc',
],
'variables': {
'test_suite_name': 'android_webview_unittests',
+ 'additional_input_paths': [
+ '<(PRODUCT_DIR)/android_webview_unittests_apk/assets/asset_file.ogg',
+ ],
},
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/android_webview_unittests_apk/assets',
+ 'files': [
+ 'test/unittest/assets/asset_file.ogg',
+ ],
+ },
+ ],
'includes': [ '../build/apk_test.gypi' ],
},
],
Send(new AwViewMsg_SetTextZoomFactor(web_contents()->GetRoutingID(), factor));
}
-void AwRenderViewHostExt::SetFixedLayoutSize(const gfx::Size& size) {
- DCHECK(CalledOnValidThread());
- Send(new AwViewMsg_SetFixedLayoutSize(web_contents()->GetRoutingID(), size));
-}
-
void AwRenderViewHostExt::ResetScrollAndScaleState() {
DCHECK(CalledOnValidThread());
Send(new AwViewMsg_ResetScrollAndScaleState(web_contents()->GetRoutingID()));
// Text Autosizing.
void SetTextZoomFactor(float factor);
- void SetFixedLayoutSize(const gfx::Size& size);
-
void ResetScrollAndScaleState();
// Sets the initial page scale. This overrides initial scale set by
IPC_MESSAGE_ROUTED1(AwViewMsg_SetInitialPageScale,
double /* page_scale_factor */)
-// Makes the blink::WebView use the given size for layout regardless of what
-// the size of the RenderWidget or viewport settings are.
-IPC_MESSAGE_ROUTED1(AwViewMsg_SetFixedLayoutSize,
- gfx::Size /* size */)
-
// Sets the base background color for this view.
IPC_MESSAGE_ROUTED1(AwViewMsg_SetBackgroundColor,
SkColor)
--- /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;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.util.Log;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+import java.io.IOException;
+
+/**
+ * A utility class to retrieve references to uncompressed assets insides the apk. A reference is
+ * defined as tuple (file descriptor, offset, size) enabling direct mapping without deflation.
+ */
+@JNINamespace("android_webview")
+public class AwAssets {
+ private static final String LOGTAG = "AwAssets";
+
+ @CalledByNative
+ public static long[] openAsset(Context context, String fileName) {
+ try {
+ AssetManager manager = context.getAssets();
+ AssetFileDescriptor 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());
+ return new long[] {-1, -1, -1};
+ }
+ }
+}
}
@Override
- public void setFixedLayoutSize(int widthDip, int heightDip) {
- if (mNativeAwContents == 0) return;
- nativeSetFixedLayoutSize(mNativeAwContents, widthDip, heightDip);
- }
-
- @Override
public boolean isLayoutParamsHeightWrapContent() {
return mContainerView.getLayoutParams() != null &&
mContainerView.getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT;
}
+
+ @Override
+ public void setForceZeroLayoutHeight(boolean forceZeroHeight) {
+ getSettings().setForceZeroLayoutHeight(forceZeroHeight);
+ }
}
//--------------------------------------------------------------------------------------------
mDIPScale = DeviceDisplayInfo.create(mContext).getDIPScale();
mLayoutSizer.setDelegate(new AwLayoutSizerDelegate());
mLayoutSizer.setDIPScale(mDIPScale);
- mWebContentsDelegate = new AwWebContentsDelegateAdapter(contentsClient, mContainerView);
+ mWebContentsDelegate = new AwWebContentsDelegateAdapter(
+ contentsClient, mContainerView, mContext);
mContentsClientBridge = new AwContentsClientBridge(contentsClient,
mBrowserContext.getKeyStore(), AwContentsStatics.getClientCertLookupTable());
mZoomControls = new AwZoomControls(this);
private native void nativeOnAttachedToWindow(long nativeAwContents, int w, int h);
private static native void nativeOnDetachedFromWindow(long nativeAwContents);
private native void nativeSetDipScale(long nativeAwContents, float dipScale);
- private native void nativeSetFixedLayoutSize(long nativeAwContents,
- int widthDip, int heightDip);
// Returns null if save state fails.
private native byte[] nativeGetOpaqueState(long nativeAwContents);
* Helper methods used to manage the layout of the View that contains AwContents.
*/
public class AwLayoutSizer {
- public static final int FIXED_LAYOUT_HEIGHT = 0;
-
// These are used to prevent a re-layout if the content size changes within a dimension that is
// fixed by the view system.
private boolean mWidthMeasurementIsFixed;
// Page scale factor. This is set to zero initially so that we don't attempt to do a layout if
// we get the content size change notification first and a page scale change second.
private float mPageScaleFactor = 0.0f;
- // The page scale factor that was used in the most recent onMeasure call.
- private float mLastMeasuredPageScaleFactor = 0.0f;
// Whether to postpone layout requests.
private boolean mFreezeLayoutRequests;
// If mHeightMeasurementLimited is true then this contains the height limit.
private int mHeightMeasurementLimit;
- // The most recent width and height seen in onSizeChanged.
- private int mLastWidth;
- private int mLastHeight;
-
- // Used to prevent sending multiple setFixedLayoutSize notifications with the same values.
- private int mLastSentFixedLayoutSizeWidth = -1;
- private int mLastSentFixedLayoutSizeHeight = -1;
-
// Callback object for interacting with the View.
private Delegate mDelegate;
+ /**
+ * Delegate interface through which the AwLayoutSizer communicates with the view it's sizing.
+ */
public interface Delegate {
void requestLayout();
void setMeasuredDimension(int measuredWidth, int measuredHeight);
- void setFixedLayoutSize(int widthDip, int heightDip);
boolean isLayoutParamsHeightWrapContent();
+ void setForceZeroLayoutHeight(boolean forceZeroHeight);
}
/**
} else {
mDelegate.requestLayout();
}
- } else if (pageScaleChanged && mLastWidth != 0) {
- // Because the fixed layout size is directly impacted by the pageScaleFactor we must
- // update it even if the physical size of the view doesn't change.
- updateFixedLayoutSize(mLastWidth, mLastHeight, mPageScaleFactor);
}
}
int measuredHeight = contentHeightPix;
int measuredWidth = contentWidthPix;
- mLastMeasuredPageScaleFactor = mPageScaleFactor;
-
// Always use the given size unless unspecified. This matches WebViewClassic behavior.
mWidthMeasurementIsFixed = (widthMode != MeasureSpec.UNSPECIFIED);
mHeightMeasurementIsFixed = (heightMode == MeasureSpec.EXACTLY);
* changed.
*/
public void onSizeChanged(int w, int h, int ow, int oh) {
- mLastWidth = w;
- mLastHeight = h;
- updateFixedLayoutSize(mLastWidth, mLastHeight, mLastMeasuredPageScaleFactor);
+ updateLayoutSettings();
}
/**
* This should be called after onSizeChanged regardless of whether the size has changed or not.
*/
public void onLayoutChange() {
- updateFixedLayoutSize(mLastWidth, mLastHeight, mLastMeasuredPageScaleFactor);
+ updateLayoutSettings();
}
- private void setFixedLayoutSize(int widthDip, int heightDip) {
- if (widthDip == mLastSentFixedLayoutSizeWidth &&
- heightDip == mLastSentFixedLayoutSizeHeight)
- return;
- mLastSentFixedLayoutSizeWidth = widthDip;
- mLastSentFixedLayoutSizeHeight = heightDip;
-
- mDelegate.setFixedLayoutSize(widthDip, heightDip);
- }
-
- // This needs to be called every time either the physical size of the view is changed or the
- // pageScale is changed. Since we need to ensure that this is called immediately after
- // onSizeChanged we can't just wait for onLayoutChange. At the same time we can't only make this
- // call from onSizeChanged, since onSizeChanged won't fire if the view's physical size doesn't
- // change.
- private void updateFixedLayoutSize(int w, int h, float pageScaleFactor) {
- boolean wrapContentForHeight = mDelegate.isLayoutParamsHeightWrapContent();
- // If the WebView's size in the Android view system depends on the size of its contents then
- // the viewport size cannot be directly calculated from the WebView's physical size as that
- // can result in the layout being unstable (for example loading the following contents
- // <div style="height:150%">a</a>
- // would cause the WebView to indefinitely attempt to increase its height by 50%).
- // If both the width and height are fixed (specified by the parent View) then content size
- // changes will not cause subsequent layout passes and so we don't need to do anything
- // special.
- // We assume the width is 'fixed' if the parent View specified an EXACT or an AT_MOST
- // measureSpec for the width (in which case the AT_MOST upper bound is the width).
- // That means that the WebView will ignore LayoutParams.width set to WRAP_CONTENT and will
- // instead try to take up as much width as possible. This is necessary because it's not
- // practical to do web layout without a set width.
- // For height the behavior is different because for a given width it is possible to
- // calculate the minimum height required to display all of the content. As such the WebView
- // can size itself vertically to match the content height. Because certain container views
- // (LinearLayout with a WRAP_CONTENT height, for example) can result in onMeasure calls with
- // both EXACTLY and AT_MOST height measureSpecs it is not possible to infer the sizing
- // policy for the whole subtree based on the parameters passed to the onMeasure call.
- // For that reason the LayoutParams.height property of the WebView is used. This behaves
- // more predictably and means that toggling the fixedLayoutSize mode (which can have
- // significant impact on how the web contents is laid out) is a direct consequence of the
- // developer's choice. The downside is that it could result in the Android layout being
- // unstable if a parent of the WebView has a wrap_content height while the WebView itself
- // has height set to match_parent. Unfortunately addressing this edge case is costly so it
- // will have to stay as is (this is compatible with Classic behavior).
- if ((mWidthMeasurementIsFixed && !wrapContentForHeight) || pageScaleFactor == 0) {
- setFixedLayoutSize(0, 0);
- return;
- }
-
- final double dipAndPageScale = pageScaleFactor * mDIPScale;
- final int contentWidthPix = (int) (mContentWidthCss * dipAndPageScale);
-
- int widthDip = (int) Math.ceil(w / dipAndPageScale);
-
- // Make sure that we don't introduce rounding errors if the viewport is to be exactly as
- // wide as the contents.
- if (w == contentWidthPix) {
- widthDip = mContentWidthCss;
- }
-
- // This is workaround due to the fact that in wrap content mode we need to use a fixed
- // layout size independent of view height, otherwise things like <div style="height:120%">
- // cause the webview to grow indefinitely. We need to use a height independent of the
- // webview's height. 0 is the value used in WebViewClassic.
- setFixedLayoutSize(widthDip, FIXED_LAYOUT_HEIGHT);
+ // This needs to be called every time either the physical size of the view is changed or layout
+ // params are updated.
+ private void updateLayoutSettings() {
+ mDelegate.setForceZeroLayoutHeight(mDelegate.isLayoutParamsHeightWrapContent());
}
}
private boolean mDomStorageEnabled = false;
private boolean mDatabaseEnabled = false;
private boolean mUseWideViewport = false;
+ private boolean mZeroLayoutHeightDisablesViewportQuirk = false;
+ private boolean mForceZeroLayoutHeight = false;
private boolean mLoadWithOverviewMode = false;
private boolean mMediaPlaybackRequiresUserGesture = true;
private String mDefaultVideoPosterURL;
return mUseWideViewport;
}
+ public void setZeroLayoutHeightDisablesViewportQuirk(boolean enabled) {
+ synchronized (mAwSettingsLock) {
+ if (mZeroLayoutHeightDisablesViewportQuirk != enabled) {
+ mZeroLayoutHeightDisablesViewportQuirk = enabled;
+ mEventHandler.updateWebkitPreferencesLocked();
+ }
+ }
+ }
+
+ public boolean getZeroLayoutHeightDisablesViewportQuirk() {
+ synchronized (mAwSettingsLock) {
+ return getZeroLayoutHeightDisablesViewportQuirkLocked();
+ }
+ }
+
+ @CalledByNative
+ private boolean getZeroLayoutHeightDisablesViewportQuirkLocked() {
+ assert Thread.holdsLock(mAwSettingsLock);
+ return mZeroLayoutHeightDisablesViewportQuirk;
+ }
+
+ public void setForceZeroLayoutHeight(boolean enabled) {
+ synchronized (mAwSettingsLock) {
+ if (mForceZeroLayoutHeight != enabled) {
+ mForceZeroLayoutHeight = enabled;
+ mEventHandler.updateWebkitPreferencesLocked();
+ }
+ }
+ }
+
+ public boolean getForceZeroLayoutHeight() {
+ synchronized (mAwSettingsLock) {
+ return getForceZeroLayoutHeightLocked();
+ }
+ }
+
+ @CalledByNative
+ private boolean getForceZeroLayoutHeightLocked() {
+ assert Thread.holdsLock(mAwSettingsLock);
+ return mForceZeroLayoutHeight;
+ }
+
@CalledByNative
private boolean getPasswordEchoEnabledLocked() {
assert Thread.holdsLock(mAwSettingsLock);
// Call in response to a prior runFileChooser call.
protected static native void nativeFilesSelectedInChooser(int processId, int renderId,
- int mode_flags, String[] filePath);
+ int mode_flags, String[] filePath, String[] displayName);
}
package org.chromium.android_webview;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
+import android.provider.MediaStore;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.ConsoleMessage;
import android.webkit.ValueCallback;
+import org.chromium.base.ContentUriUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.content.browser.ContentViewCore;
final AwContentsClient mContentsClient;
View mContainerView;
+ final Context mContext;
public AwWebContentsDelegateAdapter(AwContentsClient contentsClient,
- View containerView) {
+ View containerView, Context context) {
mContentsClient = contentsClient;
setContainerView(containerView);
+ mContext = context;
}
public void setContainerView(View containerView) {
}
@Override
- public void runFileChooser(final int processId, final int renderId, final int mode_flags,
+ public void runFileChooser(final int processId, final int renderId, final int modeFlags,
String acceptTypes, String title, String defaultFilename, boolean capture) {
AwContentsClient.FileChooserParams params = new AwContentsClient.FileChooserParams();
- params.mode = mode_flags;
+ params.mode = modeFlags;
params.acceptTypes = acceptTypes;
params.title = title;
params.defaultFilename = defaultFilename;
throw new IllegalStateException("Duplicate showFileChooser result");
}
completed = true;
- nativeFilesSelectedInChooser(processId, renderId, mode_flags, results);
+ if (results == null) {
+ nativeFilesSelectedInChooser(
+ processId, renderId, modeFlags, null, null);
+ return;
+ }
+ GetDisplayNameTask task = new GetDisplayNameTask(
+ mContext.getContentResolver(), processId, renderId, modeFlags, results);
+ task.execute();
}
}, params);
}
public void activateContents() {
mContentsClient.onRequestFocus();
}
+
+ private static class GetDisplayNameTask extends AsyncTask<Void, Void, String[]> {
+ final int mProcessId;
+ final int mRenderId;
+ final int mModeFlags;
+ final String[] mFilePaths;
+ final ContentResolver mContentResolver;
+
+ public GetDisplayNameTask(ContentResolver contentResolver, int processId, int renderId,
+ int modeFlags, String[] filePaths) {
+ mProcessId = processId;
+ mRenderId = renderId;
+ mModeFlags = modeFlags;
+ mFilePaths = filePaths;
+ mContentResolver = contentResolver;
+ }
+
+ @Override
+ protected String[] doInBackground(Void...voids) {
+ String[] displayNames = new String[mFilePaths.length];
+ for (int i = 0; i < mFilePaths.length; i++) {
+ displayNames[i] = resolveFileName(mFilePaths[i]);
+ }
+ return displayNames;
+ }
+
+ @Override
+ protected void onPostExecute(String[] result) {
+ nativeFilesSelectedInChooser(mProcessId, mRenderId, mModeFlags, mFilePaths, result);
+ }
+
+ /**
+ * @return the display name of a path if it is a content URI and is present in the database
+ * or an empty string otherwise.
+ */
+ private String resolveFileName(String filePath) {
+ if (mContentResolver == null || filePath == null) return "";
+ Uri uri = Uri.parse(filePath);
+ return ContentUriUtils.getDisplayName(
+ uri, mContentResolver, MediaStore.MediaColumns.DISPLAY_NAME);
+ }
+ }
}
final int expectedWidthCss =
(int) Math.ceil(getRootLayoutWidthOnMainThread() / deviceDIPScale);
- final int expectedHeightCss = contentHeightCss +
- // The second div in the contents is styled to have 150% of the viewport height, hence
- // the 1.5.
- (int) (AwLayoutSizer.FIXED_LAYOUT_HEIGHT * 1.5);
+ final int expectedHeightCss = contentHeightCss;
loadPageOfSizeAndWaitForSizeChange(testContainerView.getAwContents(),
mOnContentSizeChangedHelper, expectedWidthCss, contentHeightCss, true);
import org.chromium.android_webview.AwLayoutSizer;
import org.chromium.base.test.util.Feature;
+/**
+ * Unittests for the AwLayoutSizer class.
+ */
public class AwLayoutSizerTest extends InstrumentationTestCase {
static class LayoutSizerDelegate implements AwLayoutSizer.Delegate {
public int requestLayoutCallCount;
public boolean setMeasuredDimensionCalled;
public int measuredWidth;
public int measuredHeight;
- public int fixedLayoutWidth;
- public int fixedLayoutHeight;
+ public boolean forceZeroHeight;
public boolean heightWrapContent;
@Override
}
@Override
- public void setFixedLayoutSize(int widthDip, int heightDip) {
- fixedLayoutWidth = widthDip;
- fixedLayoutHeight = heightDip;
+ public void setForceZeroLayoutHeight(boolean forceZeroHeight) {
+ this.forceZeroHeight = forceZeroHeight;
}
@Override
MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY));
assertEquals(measuredWidth, delegate.measuredWidth & View.MEASURED_SIZE_MASK);
assertEquals(measuredHeight, delegate.measuredHeight & View.MEASURED_SIZE_MASK);
-
- layoutSizer.onSizeChanged(measuredWidth, measuredHeight, 0, 0);
-
- assertEquals(0, delegate.fixedLayoutWidth);
- assertEquals(0, delegate.fixedLayoutHeight);
-
- layoutSizer.onPageScaleChanged(2.0f);
-
- assertEquals(0, delegate.fixedLayoutWidth);
- assertEquals(0, delegate.fixedLayoutHeight);
}
@SmallTest
@SmallTest
@Feature({"AndroidWebView"})
- public void testViewportWithUnspecifiedMeasureSpec() {
- AwLayoutSizer layoutSizer = new AwLayoutSizer();
- LayoutSizerDelegate delegate = new LayoutSizerDelegate();
- layoutSizer.setDelegate(delegate);
-
- final float dipScale = 1.5f;
- final int pageScale = 2;
- final int dipAndPageScale = (int) (dipScale * pageScale);
-
- int contentWidth = 800;
- int contentHeight = 400;
- int atMostWidth = contentWidth * dipAndPageScale;
- int atMostHeight = contentHeight * dipAndPageScale;
-
- layoutSizer.setDIPScale(dipScale);
- layoutSizer.onContentSizeChanged(contentWidth, contentHeight);
- layoutSizer.onPageScaleChanged(pageScale);
- layoutSizer.onMeasure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-
- assertTrue(delegate.setMeasuredDimensionCalled);
- int measuredWidth = delegate.measuredWidth & View.MEASURED_SIZE_MASK;
- int measuredHeight = delegate.measuredHeight & View.MEASURED_SIZE_MASK;
-
- int sizeWidth = measuredWidth;
- int sizeHeight = measuredHeight;
- layoutSizer.onSizeChanged(sizeWidth, sizeHeight, 0, 0);
-
- assertEquals(contentWidth, delegate.fixedLayoutWidth);
- assertEquals(AwLayoutSizer.FIXED_LAYOUT_HEIGHT, delegate.fixedLayoutHeight);
-
- sizeWidth = measuredWidth * 2;
- sizeHeight = measuredHeight * 2;
- layoutSizer.onSizeChanged(sizeWidth, sizeHeight, 0, 0);
-
- assertEquals(sizeWidth / dipAndPageScale, delegate.fixedLayoutWidth);
- assertEquals(AwLayoutSizer.FIXED_LAYOUT_HEIGHT, delegate.fixedLayoutHeight);
-
- sizeWidth = measuredWidth / 2;
- sizeHeight = measuredHeight / 2;
- layoutSizer.onSizeChanged(sizeWidth, sizeHeight, 0, 0);
-
- assertEquals(sizeWidth / dipAndPageScale, delegate.fixedLayoutWidth);
- assertEquals(AwLayoutSizer.FIXED_LAYOUT_HEIGHT, delegate.fixedLayoutHeight);
- }
-
- @SmallTest
- @Feature({"AndroidWebView"})
public void testViewportWithAtMostMeasureSpec() {
AwLayoutSizer layoutSizer = new AwLayoutSizer();
LayoutSizerDelegate delegate = new LayoutSizerDelegate();
int contentWidthPix = contentWidth * dipAndPageScale;
int contentHeightPix = contentHeight * dipAndPageScale;
+ assertFalse(delegate.forceZeroHeight);
+
layoutSizer.setDIPScale(dipScale);
layoutSizer.onContentSizeChanged(contentWidth, contentHeight);
layoutSizer.onPageScaleChanged(pageScale);
MeasureSpec.makeMeasureSpec(contentHeightPix * 2, MeasureSpec.AT_MOST));
assertTrue(delegate.setMeasuredDimensionCalled);
+ assertFalse(delegate.forceZeroHeight);
+
int measuredWidth = delegate.measuredWidth & View.MEASURED_SIZE_MASK;
int measuredHeight = delegate.measuredHeight & View.MEASURED_SIZE_MASK;
+ layoutSizer.onSizeChanged(measuredWidth, measuredHeight, 0, 0);
- int sizeWidth = measuredWidth;
- int sizeHeight = measuredHeight;
- layoutSizer.onSizeChanged(sizeWidth, sizeHeight, 0, 0);
-
- assertEquals(contentWidth, delegate.fixedLayoutWidth);
- assertEquals(AwLayoutSizer.FIXED_LAYOUT_HEIGHT, delegate.fixedLayoutHeight);
- }
-
- @SmallTest
- @Feature({"AndroidWebView"})
- public void testFixedLayoutViewportGoesBackToZeroWithWrapContentMeasureSpec() {
- AwLayoutSizer layoutSizer = new AwLayoutSizer();
- LayoutSizerDelegate delegate = new LayoutSizerDelegate();
- layoutSizer.setDelegate(delegate);
- layoutSizer.setDIPScale(DIP_SCALE);
-
- layoutSizer.onContentSizeChanged(FIRST_CONTENT_WIDTH, FIRST_CONTENT_HEIGHT);
- layoutSizer.onPageScaleChanged(INITIAL_PAGE_SCALE);
- layoutSizer.onMeasure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
- layoutSizer.onSizeChanged((int) (FIRST_CONTENT_WIDTH * DIP_SCALE),
- (int) (FIRST_CONTENT_HEIGHT * DIP_SCALE), 0, 0);
-
- assertTrue(delegate.fixedLayoutWidth != 0);
- assertEquals(AwLayoutSizer.FIXED_LAYOUT_HEIGHT, delegate.fixedLayoutHeight);
-
- layoutSizer.onContentSizeChanged(FIRST_CONTENT_WIDTH, AwLayoutSizer.FIXED_LAYOUT_HEIGHT);
- layoutSizer.onSizeChanged((int) (FIRST_CONTENT_WIDTH * DIP_SCALE),
- (int) (FIRST_CONTENT_HEIGHT * DIP_SCALE), 0, 0);
- assertTrue(delegate.fixedLayoutWidth != 0);
- assertEquals(0, delegate.fixedLayoutHeight);
-
- layoutSizer.onContentSizeChanged(FIRST_CONTENT_WIDTH, 0);
- layoutSizer.onSizeChanged((int) (FIRST_CONTENT_WIDTH * DIP_SCALE),
- (int) (FIRST_CONTENT_HEIGHT * DIP_SCALE), 0, 0);
- assertTrue(delegate.fixedLayoutWidth != 0);
- assertEquals(0, delegate.fixedLayoutHeight);
+ assertTrue(delegate.forceZeroHeight);
}
@SmallTest
@Feature({"AndroidWebView"})
- public void testFixedLayoutSizeUpdatedOnPageScaleChangeItNoLayoutRequest() {
+ public void testFixedLayoutSizeDependsOnHeightWrapContent() {
AwLayoutSizer layoutSizer = new AwLayoutSizer();
LayoutSizerDelegate delegate = new LayoutSizerDelegate();
- delegate.heightWrapContent = true;
+ delegate.heightWrapContent = false;
layoutSizer.setDelegate(delegate);
layoutSizer.setDIPScale(DIP_SCALE);
MeasureSpec.makeMeasureSpec(AT_MOST_MEASURE_SIZE, MeasureSpec.AT_MOST));
layoutSizer.onSizeChanged(AT_MOST_MEASURE_SIZE, AT_MOST_MEASURE_SIZE, 0, 0);
- assertTrue(delegate.fixedLayoutWidth != 0);
- final int fixedLayoutWidth = delegate.fixedLayoutWidth;
- final int requestLayoutCallCount = delegate.requestLayoutCallCount;
- layoutSizer.onPageScaleChanged(INITIAL_PAGE_SCALE * 2f);
- assertEquals(requestLayoutCallCount, delegate.requestLayoutCallCount);
- assertEquals(fixedLayoutWidth / 2, delegate.fixedLayoutWidth);
- }
-
- @SmallTest
- @Feature({"AndroidWebView"})
- public void testFixedLayoutSizeUpdatedIfNoSizeChangeAfterLayoutRequested() {
- AwLayoutSizer layoutSizer = new AwLayoutSizer();
- LayoutSizerDelegate delegate = new LayoutSizerDelegate();
- layoutSizer.setDelegate(delegate);
- layoutSizer.setDIPScale(DIP_SCALE);
-
- layoutSizer.onContentSizeChanged(FIRST_CONTENT_WIDTH, FIRST_CONTENT_HEIGHT);
- layoutSizer.onPageScaleChanged(INITIAL_PAGE_SCALE);
- layoutSizer.onMeasure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-
- layoutSizer.onSizeChanged((int) (FIRST_CONTENT_WIDTH * DIP_SCALE),
- (int) (FIRST_CONTENT_HEIGHT * DIP_SCALE), 0, 0);
-
- assertTrue(delegate.fixedLayoutWidth != 0);
- final int fixedLayoutWidth = delegate.fixedLayoutWidth;
- final int requestLayoutCallCount = delegate.requestLayoutCallCount;
- layoutSizer.onPageScaleChanged(INITIAL_PAGE_SCALE * 0.5f);
- assertEquals(requestLayoutCallCount + 1, delegate.requestLayoutCallCount);
- assertEquals(fixedLayoutWidth, delegate.fixedLayoutWidth);
+ assertFalse(delegate.forceZeroHeight);
- // onMeasure and onLayoutChange should always be called as a result of the AwLayoutSizer
- // calling Delegate.requestLayout.
- layoutSizer.onMeasure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
- layoutSizer.onLayoutChange();
+ delegate.heightWrapContent = true;
+ layoutSizer.onSizeChanged(AT_MOST_MEASURE_SIZE, AT_MOST_MEASURE_SIZE, 0, 0);
- assertEquals(fixedLayoutWidth * 2, delegate.fixedLayoutWidth);
+ assertTrue(delegate.forceZeroHeight);
}
@SmallTest
MeasureSpec.makeMeasureSpec(AT_MOST_MEASURE_SIZE, MeasureSpec.AT_MOST));
layoutSizer.onSizeChanged(AT_MOST_MEASURE_SIZE, AT_MOST_MEASURE_SIZE, 0, 0);
- assertEquals(0, delegate.fixedLayoutWidth);
- assertEquals(0, delegate.fixedLayoutHeight);
+ assertFalse(delegate.forceZeroHeight);
+
+ layoutSizer.onMeasure(
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ layoutSizer.onSizeChanged(AT_MOST_MEASURE_SIZE, AT_MOST_MEASURE_SIZE, 0, 0);
+ assertFalse(delegate.forceZeroHeight);
}
}
private int mOnScaleChangedCallCount;
}
+ class AwSettingsForceZeroLayoutHeightTestHelper extends AwSettingsTestHelper<Boolean> {
+
+ AwSettingsForceZeroLayoutHeightTestHelper(
+ AwTestContainerView containerView,
+ TestAwContentsClient contentViewClient,
+ boolean withViewPortTag) throws Throwable {
+ super(containerView, contentViewClient, true);
+ mWithViewPortTag = withViewPortTag;
+ mAwSettings.setUseWideViewPort(true);
+ }
+
+ @Override
+ protected Boolean getAlteredValue() {
+ return ENABLED;
+ }
+
+ @Override
+ protected Boolean getInitialValue() {
+ return DISABLED;
+ }
+
+ @Override
+ protected Boolean getCurrentValue() {
+ return mAwSettings.getForceZeroLayoutHeight();
+ }
+
+ @Override
+ protected void setCurrentValue(Boolean value) {
+ mAwSettings.setForceZeroLayoutHeight(value);
+ }
+
+ @Override
+ protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
+ loadDataSync(getData());
+ int height = Integer.parseInt(getTitleOnUiThread());
+ if (value) {
+ assertEquals(0, height);
+ } else {
+ assertTrue("Div should be at least 50px high, was: " + height, height >= 50);
+ }
+ }
+
+ private String getData() {
+ return "<html><head>" +
+ (mWithViewPortTag ? "<meta name='viewport' content='height=3000' />" : "") +
+ " <script type='text/javascript'> " +
+ " window.addEventListener('load', function(event) { " +
+ " document.title = document.getElementById('testDiv').clientHeight; " +
+ " }); " +
+ " </script> " +
+ "</head>" +
+ "<body> " +
+ " <div style='height:50px;'>test</div> " +
+ " <div id='testDiv' style='height:100%;'></div> " +
+ "</body></html>";
+ }
+
+ private final boolean mWithViewPortTag;
+ }
+
+ class AwSettingsZeroLayoutHeightDisablesViewportQuirkTestHelper extends
+ AwSettingsTestHelper<Boolean> {
+
+ AwSettingsZeroLayoutHeightDisablesViewportQuirkTestHelper(
+ AwTestContainerView containerView,
+ TestAwContentsClient contentViewClient) throws Throwable {
+ super(containerView, contentViewClient, true);
+ mAwSettings.setUseWideViewPort(true);
+ mAwSettings.setForceZeroLayoutHeight(true);
+ }
+
+ @Override
+ protected Boolean getAlteredValue() {
+ return ENABLED;
+ }
+
+ @Override
+ protected Boolean getInitialValue() {
+ return DISABLED;
+ }
+
+ @Override
+ protected Boolean getCurrentValue() {
+ return mAwSettings.getZeroLayoutHeightDisablesViewportQuirk();
+ }
+
+ @Override
+ protected void setCurrentValue(Boolean value) {
+ mAwSettings.setZeroLayoutHeightDisablesViewportQuirk(value);
+ }
+
+ @Override
+ protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
+ DeviceDisplayInfo deviceInfo = DeviceDisplayInfo.create(mContext);
+ int displayWidth = (int) (deviceInfo.getDisplayWidth() / deviceInfo.getDIPScale());
+
+ loadDataSync(getData());
+ int width = Integer.parseInt(getTitleOnUiThread());
+ if (value) {
+ assertEquals(displayWidth, width);
+ } else {
+ assertEquals(3000, width);
+ }
+ }
+
+ private String getData() {
+ return "<html><head>" +
+ "<meta name='viewport' content='width=3000' />" +
+ " <script type='text/javascript'> " +
+ " window.addEventListener('load', function(event) { " +
+ " document.title = document.documentElement.clientWidth; " +
+ " }); " +
+ " </script> " +
+ "</head>" +
+ "<body> " +
+ " <div style='height:50px;'>test</div> " +
+ " <div id='testDiv' style='height:100%;'></div> " +
+ "</body></html>";
+ }
+ }
+
// The test verifies that JavaScript is disabled upon WebView
// creation without accessing AwSettings. If the test passes,
// it means that WebView-specific web preferences configuration
@SmallTest
@Feature({"AndroidWebView", "Preferences"})
+ public void testForceZeroLayoutHeightWithTwoViews() throws Throwable {
+ ViewPair views = createViews();
+ runPerViewSettingsTest(
+ new AwSettingsForceZeroLayoutHeightTestHelper(
+ views.getContainer0(), views.getClient0(), false),
+ new AwSettingsForceZeroLayoutHeightTestHelper(
+ views.getContainer1(), views.getClient1(), false));
+ }
+
+ @SmallTest
+ @Feature({"AndroidWebView", "Preferences"})
+ public void testForceZeroLayoutHeightViewportTagWithTwoViews() throws Throwable {
+ ViewPair views = createViews();
+ runPerViewSettingsTest(
+ new AwSettingsForceZeroLayoutHeightTestHelper(
+ views.getContainer0(), views.getClient0(), true),
+ new AwSettingsForceZeroLayoutHeightTestHelper(
+ views.getContainer1(), views.getClient1(), true));
+ }
+
+ @SmallTest
+ @Feature({"AndroidWebView", "Preferences"})
+ public void testZeroLayoutHeightDisablesViewportQuirkWithTwoViews() throws Throwable {
+ ViewPair views = createViews();
+ runPerViewSettingsTest(
+ new AwSettingsZeroLayoutHeightDisablesViewportQuirkTestHelper(
+ views.getContainer0(), views.getClient0()),
+ new AwSettingsZeroLayoutHeightDisablesViewportQuirkTestHelper(
+ views.getContainer1(), views.getClient1()));
+ }
+
+ @SmallTest
+ @Feature({"AndroidWebView", "Preferences"})
public void testLoadWithOverviewModeWithTwoViews() throws Throwable {
ViewPair views = createViews();
runPerViewSettingsTest(
#include "android_webview/browser/gpu_memory_buffer_factory_impl.h"
#include "android_webview/browser/scoped_allow_wait_for_legacy_web_view_api.h"
#include "android_webview/lib/aw_browser_dependency_factory_impl.h"
+#include "android_webview/native/aw_media_url_interceptor.h"
#include "android_webview/native/aw_quota_manager_bridge_impl.h"
#include "android_webview/native/aw_web_contents_view_delegate.h"
#include "android_webview/native/aw_web_preferences_populater_impl.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/thread_restrictions.h"
+#include "content/browser/media/android/browser_media_player_manager.h"
#include "content/public/browser/browser_main_runner.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_switches.h"
bool AwMainDelegate::BasicStartupComplete(int* exit_code) {
content::SetContentClient(&content_client_);
+
+ CommandLine* cl = CommandLine::ForCurrentProcess();
gpu::InProcessCommandBuffer::SetGpuMemoryBufferFactory(
gpu_memory_buffer_factory_.get());
+ content::BrowserMediaPlayerManager::RegisterMediaUrlInterceptor(
+ new AwMediaUrlInterceptor());
+
BrowserViewRenderer::CalculateTileMemoryPolicy();
- CommandLine* cl = CommandLine::ForCurrentProcess();
cl->AppendSwitch(switches::kEnableBeginFrameScheduling);
cl->AppendSwitch(switches::kEnableZeroCopy);
cl->AppendSwitch(switches::kEnableImplSidePainting);
include_rules = [
"+content/public/browser",
"+content/public/test",
+ "+media/base/android",
"+ui/gfx",
"+ui/shell_dialogs",
#include "android_webview/native/android_webview_jni_registrar.h"
#include "android_webview/native/android_protocol_handler.h"
+#include "android_webview/native/aw_assets.h"
#include "android_webview/native/aw_autofill_client.h"
#include "android_webview/native/aw_contents.h"
#include "android_webview/native/aw_contents_client_bridge.h"
// Register JNI for android_webview classes.
{ "AndroidProtocolHandler", RegisterAndroidProtocolHandler },
{ "AwAutofillClient", RegisterAwAutofillClient },
+ { "AwAssets", RegisterAwAssets },
{ "AwContents", RegisterAwContents },
{ "AwContentsClientBridge", RegisterAwContentsClientBridge },
{ "AwContentsIoThreadClientImpl", RegisterAwContentsIoThreadClientImpl },
--- /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 <jni.h>
+
+#include "android_webview/native/aw_assets.h"
+
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "jni/AwAssets_jni.h"
+
+namespace android_webview {
+namespace AwAssets {
+
+bool OpenAsset(const std::string& filename,
+ int* fd,
+ int64* offset,
+ int64* size) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ ScopedJavaLocalRef<jlongArray> jarr = Java_AwAssets_openAsset(
+ env,
+ base::android::GetApplicationContext(),
+ base::android::ConvertUTF8ToJavaString(env, filename).Release());
+ std::vector<long> results;
+ base::android::JavaLongArrayToLongVector(env, jarr.obj(), &results);
+ DCHECK_EQ(3U, results.size());
+ *fd = static_cast<int>(results[0]);
+ *offset = results[1];
+ *size = results[2];
+ return *fd != -1;
+}
+
+} // namespace AwAssets
+
+bool RegisterAwAssets(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // 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_NATIVE_AW_ASSETS_H_
+#define ANDROID_WEBVIEW_NATIVE_AW_ASSETS_H_
+
+#include <string>
+
+#include "base/android/jni_android.h"
+
+namespace android_webview {
+namespace AwAssets {
+
+// Called by native to retrieve an asset (e.g. a .pak file) from the apk.
+// Returns: true in case of success, false otherwise.
+// Output arguments:
+// - |fd|: file descriptor to the apk. The caller takes the ownership.
+// - |offset|: offset in bytes from the start of the file
+// - |size|: size in bytes of the asset / resource.
+bool OpenAsset(const std::string& filename,
+ int* fd,
+ int64* offset,
+ int64* size);
+
+} // namespace AwAssets
+
+bool RegisterAwAssets(JNIEnv* env);
+
+} // namsespace android_webview
+
+#endif // ANDROID_WEBVIEW_NATIVE_AW_ASSETS_H_
browser_view_renderer_.SetDipScale(dip_scale);
}
-void AwContents::SetFixedLayoutSize(JNIEnv* env,
- jobject obj,
- jint width_dip,
- jint height_dip) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- render_view_host_ext_->SetFixedLayoutSize(gfx::Size(width_dip, height_dip));
-}
-
void AwContents::ScrollTo(JNIEnv* env, jobject obj, jint x, jint y) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
browser_view_renderer_.ScrollTo(gfx::Vector2d(x, y));
void ScrollTo(JNIEnv* env, jobject obj, jint x, jint y);
void SetDipScale(JNIEnv* env, jobject obj, jfloat dip_scale);
- void SetFixedLayoutSize(JNIEnv* env,
- jobject obj,
- jint width_dip,
- jint height_dip);
void SetSaveFormData(bool enabled);
// Sets the java client
--- /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 <string>
+
+#include "android_webview/common/url_constants.h"
+#include "android_webview/native/aw_assets.h"
+#include "android_webview/native/aw_media_url_interceptor.h"
+#include "base/strings/string_util.h"
+#include "content/public/common/url_constants.h"
+
+namespace android_webview {
+
+bool AwMediaUrlInterceptor::Intercept(const std::string& url,
+ int* fd, int64* offset, int64* size) const{
+ const std::string asset_file_prefix(
+ std::string(url::kFileScheme) +
+ std::string(url::kStandardSchemeSeparator) +
+ android_webview::kAndroidAssetPath);
+
+ if (StartsWithASCII(url, asset_file_prefix, true)) {
+ std::string filename(url);
+ ReplaceFirstSubstringAfterOffset(&filename, 0, asset_file_prefix, "");
+ return AwAssets::OpenAsset(filename, fd, offset, size);
+ }
+
+ return false;
+}
+
+} // 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_NATIVE_AW_MEDIA_URL_INTERCEPTOR_H_
+#define ANDROID_WEBVIEW_NATIVE_AW_MEDIA_URL_INTERCEPTOR_H_
+
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "media/base/android/media_url_interceptor.h"
+
+namespace android_webview {
+
+// Interceptor to handle urls for media assets in the apk.
+class AwMediaUrlInterceptor : public media::MediaUrlInterceptor {
+ public:
+ virtual bool Intercept(const std::string& url,
+ int* fd,
+ int64* offset,
+ int64* size) const OVERRIDE;
+};
+
+} // namespace android_webview
+
+#endif // ANDROID_WEBVIEW_NATIVE_AW_MEDIA_URL_INTERCEPTOR_H_
--- /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 <string>
+
+#include "android_webview/native/aw_media_url_interceptor.h"
+#include "base/memory/scoped_ptr.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Test;
+
+namespace android_webview {
+
+namespace {
+
+// Sentinel value to check whether the fields have been set.
+const int UNSET_VALUE = -1;
+
+class AwMediaUrlInterceptorTest : public Test {
+ public:
+ AwMediaUrlInterceptorTest()
+ : fd_(UNSET_VALUE), offset_(UNSET_VALUE), size_(UNSET_VALUE),
+ url_interceptor_(new AwMediaUrlInterceptor()){
+ }
+ protected:
+ int fd_;
+ int64 offset_;
+ int64 size_;
+ scoped_ptr<AwMediaUrlInterceptor> url_interceptor_;
+};
+
+} // namespace
+
+TEST_F(AwMediaUrlInterceptorTest, TestInterceptValidAssetUrl) {
+ // This asset file exists in the android_webview_unittests-debug.apk.
+ // See gyp rule android_webview_unittests_apk.
+ const std::string valid_asset_url(
+ "file:///android_asset/asset_file.ogg");
+
+ ASSERT_TRUE(url_interceptor_->Intercept(
+ valid_asset_url, &fd_, &offset_, &size_));
+ EXPECT_NE(UNSET_VALUE, fd_);
+ EXPECT_NE(UNSET_VALUE, offset_);
+ EXPECT_NE(UNSET_VALUE, size_);
+}
+
+TEST_F(AwMediaUrlInterceptorTest, TestInterceptInvalidAssetUrl) {
+ // This asset file does not exist in the android_webview_unittests-debug.apk.
+ // See gyp rule android_webview_unittests_apk.
+ const std::string invalid_asset_url(
+ "file:///android_asset/file_does_not_exist.ogg");
+
+ ASSERT_FALSE(url_interceptor_->Intercept(
+ invalid_asset_url, &fd_, &offset_, &size_));
+ EXPECT_EQ(UNSET_VALUE, fd_);
+ EXPECT_EQ(UNSET_VALUE, offset_);
+ EXPECT_EQ(UNSET_VALUE, size_);
+}
+
+TEST_F(AwMediaUrlInterceptorTest, TestInterceptNonAssetUrl) {
+ // This url does not refer to an asset in the apk.
+ const std::string non_asset_url("file:///sdcard/file.txt");
+
+ ASSERT_FALSE(url_interceptor_->Intercept(
+ non_asset_url, &fd_, &offset_, &size_));
+ EXPECT_EQ(UNSET_VALUE, fd_);
+ EXPECT_EQ(UNSET_VALUE, offset_);
+ EXPECT_EQ(UNSET_VALUE, size_);
+}
+
+} // namespace android_webview
web_prefs->use_wide_viewport =
Java_AwSettings_getUseWideViewportLocked(env, obj);
+ web_prefs->force_zero_layout_height =
+ Java_AwSettings_getForceZeroLayoutHeightLocked(env, obj);
+
+ const bool zero_layout_height_disables_viewport_quirk =
+ Java_AwSettings_getZeroLayoutHeightDisablesViewportQuirkLocked(env, obj);
+ web_prefs->viewport_enabled = !(zero_layout_height_disables_viewport_quirk &&
+ web_prefs->force_zero_layout_height);
+
web_prefs->double_tap_to_zoom_enabled =
Java_AwSettings_supportsDoubleTapZoomLocked(env, obj);
static void FilesSelectedInChooser(
JNIEnv* env, jclass clazz,
jint process_id, jint render_id, jint mode_flags,
- jobjectArray file_paths) {
+ jobjectArray file_paths, jobjectArray display_names) {
content::RenderViewHost* rvh = content::RenderViewHost::FromID(process_id,
render_id);
if (!rvh)
return;
std::vector<std::string> file_path_str;
+ std::vector<std::string> display_name_str;
// Note file_paths maybe NULL, but this will just yield a zero-length vector.
base::android::AppendJavaStringArrayToStringVector(env, file_paths,
&file_path_str);
+ base::android::AppendJavaStringArrayToStringVector(env, display_names,
+ &display_name_str);
std::vector<ui::SelectedFileInfo> files;
files.reserve(file_path_str.size());
for (size_t i = 0; i < file_path_str.size(); ++i) {
if (!url.is_valid())
continue;
base::FilePath path(url.SchemeIsFile() ? url.path() : file_path_str[i]);
- files.push_back(ui::SelectedFileInfo(path, base::FilePath()));
+ ui::SelectedFileInfo file_info(path, base::FilePath());
+ if (!display_name_str[i].empty())
+ file_info.display_name = display_name_str[i];
+ files.push_back(file_info);
}
FileChooserParams::Mode mode;
if (mode_flags & kFileChooserModeOpenFolder) {
'../../components/components.gyp:autofill_content_browser',
'../../components/components.gyp:web_contents_delegate_android',
'../../content/content.gyp:content_common',
+ '../../media/media.gyp:player_android',
'../../net/net.gyp:net',
'../../skia/skia.gyp:skia',
'../../ui/base/ui_base.gyp:ui_base',
'android_protocol_handler.h',
'android_webview_jni_registrar.cc',
'android_webview_jni_registrar.h',
+ 'aw_assets.cc',
+ 'aw_assets.h',
'aw_autofill_client.cc',
'aw_autofill_client.h',
'aw_browser_dependency_factory.cc',
'aw_form_database.h',
'aw_http_auth_handler.cc',
'aw_http_auth_handler.h',
+ 'aw_media_url_interceptor.cc',
+ 'aw_media_url_interceptor.h',
'aw_pdf_exporter.cc',
'aw_pdf_exporter.h',
'aw_picture.cc',
'type': 'none',
'sources': [
'../java/src/org/chromium/android_webview/AndroidProtocolHandler.java',
+ '../java/src/org/chromium/android_webview/AwAssets.java',
'../java/src/org/chromium/android_webview/AwAutofillClient.java',
'../java/src/org/chromium/android_webview/AwContents.java',
'../java/src/org/chromium/android_webview/AwContentsClientBridge.java',
IPC_MESSAGE_HANDLER(AwViewMsg_ResetScrollAndScaleState,
OnResetScrollAndScaleState)
IPC_MESSAGE_HANDLER(AwViewMsg_SetInitialPageScale, OnSetInitialPageScale)
- IPC_MESSAGE_HANDLER(AwViewMsg_SetFixedLayoutSize, OnSetFixedLayoutSize)
IPC_MESSAGE_HANDLER(AwViewMsg_SetBackgroundColor, OnSetBackgroundColor)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
page_scale_factor);
}
-void AwRenderViewExt::OnSetFixedLayoutSize(const gfx::Size& size) {
- if (!render_view() || !render_view()->GetWebView())
- return;
- render_view()->GetWebView()->setFixedLayoutSize(size);
-}
-
void AwRenderViewExt::OnSetBackgroundColor(SkColor c) {
if (!render_view() || !render_view()->GetWebView())
return;
void OnSetInitialPageScale(double page_scale_factor);
- void OnSetFixedLayoutSize(const gfx::Size& size);
-
void OnSetBackgroundColor(SkColor c);
void UpdatePageScaleFactor();
--- /dev/null
+This file is used by the WebView unittests for loading media asset files,
+see android_webview_unittests_apk and AwMediaUrlInterceptorTest.
+
+This is a text file, but we use the .ogg extension so that aapt will not
+compress its contents. This is ok for the purpose of the unit tests since
+they do not play the media, and they are only interested in the existance of
+an uncompressed media file in the assets directory.
<translation id="1882897271359938046">نسخ إلى <ph name="DISPLAY_NAME"/></translation>
<translation id="2727977024730340865">تمّ توصيل شاحن منخفض الطاقة. لذا قد لا تكون عملية شحن البطارية جديرة بالثقة.</translation>
<translation id="3784455785234192852">قفل</translation>
-<translation id="2805756323405976993">اÙ\84تطبÙ\8aÙ\82ات</translation>
+<translation id="2805756323405976993">تطبيقات</translation>
<translation id="1512064327686280138">إخفاق في عملية التنشيط</translation>
<translation id="5097002363526479830">أخفق الاتصال بشبكة "<ph name="NAME"/>": <ph name="DETAILS"/></translation>
<translation id="1850504506766569011">تم إيقاف تشغيل Wi-Fi.</translation>
<translation id="1882897271359938046">Nagmi-mirror sa <ph name="DISPLAY_NAME"/></translation>
<translation id="2727977024730340865">Naka-saksak sa isang low-power charger. Maaaring hindi maging tiyak ang pag-charge ng baterya.</translation>
<translation id="3784455785234192852">I-lock</translation>
-<translation id="2805756323405976993">Apps</translation>
+<translation id="2805756323405976993">Mga App</translation>
<translation id="1512064327686280138">Pagkabigo ng pag-activate</translation>
<translation id="5097002363526479830">Nabigong kumonekta sa network na '<ph name="NAME"/>': <ph name="DETAILS"/></translation>
<translation id="1850504506766569011">Naka-off ang Wi-Fi.</translation>
<translation id="1882897271359938046">Mise en miroir pour <ph name="DISPLAY_NAME"/></translation>
<translation id="2727977024730340865">L'appareil est branché à un chargeur de faible puissance. Il se peut que la charge ne soit pas fiable.</translation>
<translation id="3784455785234192852">Verrouiller</translation>
-<translation id="2805756323405976993">Google Apps</translation>
+<translation id="2805756323405976993">Applications</translation>
<translation id="1512064327686280138">Échec de l'activation</translation>
<translation id="5097002363526479830">Échec de la connexion au réseau "<ph name="NAME"/>" : <ph name="DETAILS"/>.</translation>
<translation id="1850504506766569011">Le Wi-Fi est désactivé.</translation>
<translation id="1882897271359938046"><ph name="DISPLAY_NAME"/> પર પ્રતિબિંબિત થઈ રહ્યું છે</translation>
<translation id="2727977024730340865">નિમ્ન-પાવર ચાર્જરમાં પ્લગ કરેલું છે. બૅટરી ચાર્જિંગ વિશ્વસનીય હશે નહીં.</translation>
<translation id="3784455785234192852">લૉક</translation>
-<translation id="2805756323405976993">àª\8fપà«\8dલિàª\95à«\87શનà«\8dસ</translation>
+<translation id="2805756323405976993">એપ્સ</translation>
<translation id="1512064327686280138">સક્રિયતા નિષ્ફળ</translation>
<translation id="5097002363526479830">નેટવર્ક '<ph name="NAME"/>' થી કનેક્ટ કરવામાં નિષ્ફળ: <ph name="DETAILS"/></translation>
<translation id="1850504506766569011">Wi-Fi બંધ છે.</translation>
<translation id="1882897271359938046">Zrcaljenje na zaslon <ph name="DISPLAY_NAME"/></translation>
<translation id="2727977024730340865">Uređaj je priključen na punjač male snage. Punjenje baterije možda nije pouzdano.</translation>
<translation id="3784455785234192852">Zaključaj</translation>
-<translation id="2805756323405976993">Apps</translation>
+<translation id="2805756323405976993">Aplikacije</translation>
<translation id="1512064327686280138">Neuspjela aktivacija</translation>
<translation id="5097002363526479830">Neuspješno povezivanje s mrežom "<ph name="NAME"/>": <ph name="DETAILS"/></translation>
<translation id="1850504506766569011">Wi-Fi je isključen.</translation>
<translation id="1882897271359938046">Mencerminkan ke <ph name="DISPLAY_NAME"/></translation>
<translation id="2727977024730340865">Dipasang ke pengisi daya rendah. Pengisian daya baterai mungkin tidak dapat diandalkan.</translation>
<translation id="3784455785234192852">Kunci</translation>
-<translation id="2805756323405976993">Apps</translation>
+<translation id="2805756323405976993">Apl</translation>
<translation id="1512064327686280138">Kegagalan aktivasi</translation>
<translation id="5097002363526479830">Gagal menyambung ke jaringan '<ph name="NAME"/>': <ph name="DETAILS"/></translation>
<translation id="1850504506766569011">Wi-Fi dinonaktifkan.</translation>
<translation id="1882897271359938046">Mirroring su <ph name="DISPLAY_NAME"/></translation>
<translation id="2727977024730340865">Collegato a un caricabatterie a basso consumo. La carica della batteria potrebbe non essere affidabile.</translation>
<translation id="3784455785234192852">Blocca</translation>
-<translation id="2805756323405976993">Google Apps</translation>
+<translation id="2805756323405976993">App</translation>
<translation id="1512064327686280138">Errore di attivazione</translation>
<translation id="5097002363526479830">Connessione alla rete "<ph name="NAME"/>" non riuscita: <ph name="DETAILS"/></translation>
<translation id="1850504506766569011">Wi-Fi non attivo.</translation>
<translation id="1882897271359938046">משקף אל <ph name="DISPLAY_NAME"/></translation>
<translation id="2727977024730340865">מחובר למטען בעל מתח נמוך. ייתכן שטעינת הסוללה לא תהיה אמינה.</translation>
<translation id="3784455785234192852">נעילה</translation>
-<translation id="2805756323405976993">Apps</translation>
+<translation id="2805756323405976993">אפליקציות</translation>
<translation id="1512064327686280138">כשל בהפעלה</translation>
<translation id="5097002363526479830">ההתחברות לרשת נכשלה '<ph name="NAME"/>': <ph name="DETAILS"/></translation>
<translation id="1850504506766569011">Wi-Fi כבוי.</translation>
<translation id="1882897271359938046"><ph name="DISPLAY_NAME"/> ಗೆ ಪ್ರತಿಬಿಂಬಿಸುತ್ತಿದೆ</translation>
<translation id="2727977024730340865">ಕಡಿಮೆ ವಿದ್ಯುತ್ ಚಾರ್ಜರ್ಗೆ ಪ್ಲಗ್ ಮಾಡಲಾಗಿದೆ. ಬ್ಯಾಟರಿ ಚಾರ್ಜಿಂಗ್ ವಿಶ್ವಾಸಾರ್ಹವಾಗಿಲ್ಲದಿರಬಹುದು.</translation>
<translation id="3784455785234192852">ಲಾಕ್ ಮಾಡಿ</translation>
-<translation id="2805756323405976993">Apps</translation>
+<translation id="2805756323405976993">ಆಪ್ಸ್</translation>
<translation id="1512064327686280138">ಸಕ್ರಿಯಗೊಳಿಸುವಿಕೆ ವಿಫಲವಾಗಿದೆ</translation>
<translation id="5097002363526479830">'<ph name="NAME"/>' ನೆಟ್ವರ್ಕ್ಗೆ ಸಂಪರ್ಕಿಸಲು ವಿಫಲವಾಗಿದೆ: <ph name="DETAILS"/></translation>
<translation id="1850504506766569011">Wi-Fi ಆಫ್ ಮಾಡಲಾಗಿದೆ.</translation>
<translation id="1882897271359938046"><ph name="DISPLAY_NAME"/>에 미러링</translation>
<translation id="2727977024730340865">저출력 충전기에 연결되었습니다. 배터리 충전 상태가 불안정합니다.</translation>
<translation id="3784455785234192852">잠금</translation>
-<translation id="2805756323405976993">ì\9d\91ì\9a©í\94\84ë¡\9cê·¸ë\9e¨</translation>
+<translation id="2805756323405976993">ì\95±</translation>
<translation id="1512064327686280138">활성화 실패</translation>
<translation id="5097002363526479830">'<ph name="NAME"/>' 네트워크에 연결하지 못했습니다: <ph name="DETAILS"/></translation>
<translation id="1850504506766569011">Wi-Fi가 꺼져 있습니다.</translation>
<translation id="1882897271359938046">Spiegelen naar <ph name="DISPLAY_NAME"/></translation>
<translation id="2727977024730340865">Aangesloten op een laag-vermogen-lader. Opladen van de batterij mogelijk niet betrouwbaar.</translation>
<translation id="3784455785234192852">Vergrendelen</translation>
-<translation id="2805756323405976993">Google Apps</translation>
+<translation id="2805756323405976993">Apps</translation>
<translation id="1512064327686280138">Activering mislukt</translation>
<translation id="5097002363526479830">Kan geen verbinding maken met het netwerk '<ph name="NAME"/>': <ph name="DETAILS"/></translation>
<translation id="1850504506766569011">Wifi is uitgeschakeld.</translation>
if (event->phase() != ui::EP_PRETARGET)
return;
- CompleteDrag(event->type() == ui::ET_MOUSE_RELEASED ?
- DRAG_COMPLETE : DRAG_REVERT);
+ if (window_resizer_) {
+ CompleteDrag(event->type() == ui::ET_MOUSE_RELEASED ?
+ DRAG_COMPLETE : DRAG_REVERT);
+ }
+
// Completing the drag may result in hiding the window. If this happens
// mark the event as handled so no other handlers/observers act upon the
// event. They should see the event on a hidden window, to determine targets
#include "ash/wm/workspace_controller.h"
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "base/message_loop/message_loop.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/test/aura_test_base.h"
EXPECT_EQ("10,11 100x100", window1->bounds().ToString());
}
+namespace {
+
+void SendMouseReleaseAndReleaseCapture(aura::test::EventGenerator* generator,
+ aura::Window* window) {
+ generator->ReleaseLeftButton();
+ window->ReleaseCapture();
+}
+
+} // namespace
+
+// Test that a drag is successful even if ET_MOUSE_CAPTURE_CHANGED is sent
+// immediately after the mouse release. views::Widget has this behavior.
+TEST_F(ToplevelWindowEventHandlerTest, CaptureLossAfterMouseRelease) {
+ scoped_ptr<aura::Window> window(CreateWindow(HTNOWHERE));
+ aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
+ window.get());
+ generator.PressLeftButton();
+ window->SetCapture();
+
+ aura::client::WindowMoveClient* move_client =
+ aura::client::GetWindowMoveClient(window->GetRootWindow());
+ base::MessageLoopForUI::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&SendMouseReleaseAndReleaseCapture,
+ base::Unretained(&generator),
+ base::Unretained(window.get())));
+ EXPECT_EQ(aura::client::MOVE_SUCCESSFUL,
+ move_client->RunMoveLoop(window.get(), gfx::Vector2d(),
+ aura::client::WINDOW_MOVE_SOURCE_MOUSE));
+}
+
// Showing the resize shadows when the mouse is over the window edges is tested
// in resize_shadow_and_cursor_test.cc
import android.content.ContentResolver;
import android.content.Context;
+import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;
/**
* This class provides methods to access content URI schemes.
*/
-abstract class ContentUriUtils {
+public abstract class ContentUriUtils {
private static final String TAG = "ContentUriUtils";
// Prevent instantiation.
}
return pfd;
}
+
+ /**
+ * Method to resolve the display name of a content URI.
+ *
+ * @param uri the content URI to be resolved.
+ * @param contentResolver the content resolver to query.
+ * @param columnField the column field to query.
+ * @returns the display name of the @code uri if present in the database
+ * or an empty string otherwise.
+ */
+ public static String getDisplayName(
+ Uri uri, ContentResolver contentResolver, String columnField) {
+ if (contentResolver == null || uri == null) return "";
+ Cursor cursor = null;
+ try {
+ cursor = contentResolver.query(uri, null, null, null, null);
+
+ if (cursor != null && cursor.getCount() >= 1) {
+ cursor.moveToFirst();
+ int index = cursor.getColumnIndex(columnField);
+ if (index > -1) return cursor.getString(index);
+ }
+ } catch (NullPointerException e) {
+ // Some android models don't handle the provider call correctly.
+ // see crbug.com/345393
+ return "";
+ } finally {
+ if (cursor != null) cursor.close();
+ }
+ return "";
+ }
}
env->ReleaseIntArrayElements(int_array, ints, JNI_ABORT);
}
+void JavaLongArrayToLongVector(JNIEnv* env,
+ jlongArray long_array,
+ std::vector<long>* out) {
+ DCHECK(out);
+ out->clear();
+ jsize len = env->GetArrayLength(long_array);
+ jlong* longs = env->GetLongArrayElements(long_array, NULL);
+ for (jsize i = 0; i < len; ++i) {
+ out->push_back(static_cast<long>(longs[i]));
+ }
+ env->ReleaseLongArrayElements(long_array, longs, JNI_ABORT);
+}
+
void JavaFloatArrayToFloatVector(JNIEnv* env,
jfloatArray float_array,
std::vector<float>* out) {
jintArray int_array,
std::vector<int>* out);
+// Replaces the content of |out| with the Java longs in |long_array|.
+BASE_EXPORT void JavaLongArrayToLongVector(
+ JNIEnv* env,
+ jlongArray long_array,
+ std::vector<long>* out);
+
// Replaces the content of |out| with the Java floats in |float_array|.
BASE_EXPORT void JavaFloatArrayToFloatVector(
JNIEnv* env,
iat_function.thunk = *iat_thunk;
return iat_function.pointer;
}
-// Change the page protection (of code pages) to writable and copy
-// the data at the specified location
-//
-// Arguments:
-// old_code Target location to copy
-// new_code Source
-// length Number of bytes to copy
-//
-// Returns: Windows error code (winerror.h). NO_ERROR if successful
-DWORD ModifyCode(void* old_code, void* new_code, int length) {
- if ((NULL == old_code) || (NULL == new_code) || (0 == length)) {
- NOTREACHED();
- return ERROR_INVALID_PARAMETER;
- }
-
- // Change the page protection so that we can write.
- MEMORY_BASIC_INFORMATION memory_info;
- DWORD error = NO_ERROR;
- DWORD old_page_protection = 0;
-
- if (!VirtualQuery(old_code, &memory_info, sizeof(memory_info))) {
- error = GetLastError();
- return error;
- }
-
- DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
- PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
- memory_info.Protect;
-
- if (VirtualProtect(old_code,
- length,
- is_executable ? PAGE_EXECUTE_READWRITE :
- PAGE_READWRITE,
- &old_page_protection)) {
-
- // Write the data.
- CopyMemory(old_code, new_code, length);
-
- // Restore the old page protection.
- error = ERROR_SUCCESS;
- VirtualProtect(old_code,
- length,
- old_page_protection,
- &old_page_protection);
- } else {
- error = GetLastError();
- }
-
- return error;
-}
bool InterceptEnumCallback(const base::win::PEImage& image, const char* module,
DWORD ordinal, const char* name, DWORD hint,
} // namespace
+// Change the page protection (of code pages) to writable and copy
+// the data at the specified location
+//
+// Arguments:
+// old_code Target location to copy
+// new_code Source
+// length Number of bytes to copy
+//
+// Returns: Windows error code (winerror.h). NO_ERROR if successful
+DWORD ModifyCode(void* old_code, void* new_code, int length) {
+ if ((NULL == old_code) || (NULL == new_code) || (0 == length)) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ // Change the page protection so that we can write.
+ MEMORY_BASIC_INFORMATION memory_info;
+ DWORD error = NO_ERROR;
+ DWORD old_page_protection = 0;
+
+ if (!VirtualQuery(old_code, &memory_info, sizeof(memory_info))) {
+ error = GetLastError();
+ return error;
+ }
+
+ DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
+ PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
+ memory_info.Protect;
+
+ if (VirtualProtect(old_code,
+ length,
+ is_executable ? PAGE_EXECUTE_READWRITE :
+ PAGE_READWRITE,
+ &old_page_protection)) {
+
+ // Write the data.
+ CopyMemory(old_code, new_code, length);
+
+ // Restore the old page protection.
+ error = ERROR_SUCCESS;
+ VirtualProtect(old_code,
+ length,
+ old_page_protection,
+ &old_page_protection);
+ } else {
+ error = GetLastError();
+ }
+
+ return error;
+}
+
IATPatchFunction::IATPatchFunction()
: module_handle_(NULL),
original_function_(NULL),
void* original_function() const;
+
private:
HMODULE module_handle_;
void* intercept_function_;
DISALLOW_COPY_AND_ASSIGN(IATPatchFunction);
};
+BASE_EXPORT DWORD ModifyCode(void* old_code, void* new_code, int length);
+
} // namespace win
} // namespace base
MAJOR=37
MINOR=0
BUILD=2062
-PATCH=76
+PATCH=94
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (c) 2013 The Chromium Authors. All rights reserved.
+<!--
+ Copyright (c) 2013 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="@dimen/certificate_viewer_padding"
- android:paddingBottom="@dimen/certificate_viewer_padding">
- <ImageView android:id="@+id/website_settings_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/certificate_viewer_padding"/>
- <LinearLayout android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/certificate_viewer_padding"
- android:paddingEnd="@dimen/certificate_viewer_padding">
- <TextView android:id="@+id/website_settings_headline"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textStyle="bold"/>
- <TextView android:id="@+id/website_settings_description"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
-</LinearLayout>
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@+id/website_settings_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <LinearLayout
+ android:id="@+id/website_settings_text_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingStart="@dimen/certificate_viewer_padding_thin" >
+
+ <TextView
+ android:id="@+id/website_settings_headline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/certificate_viewer_padding_thin"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/website_settings_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <!-- Menu Dimensions -->
+ <!-- Necessary to align the menu icon with the actual button. -->
+ <dimen name="menu_software_vertical_offset">-6dp</dimen>
+</resources>
\ No newline at end of file
<color name="app_banner_install_button_fg">#ffffff</color>
<color name="app_banner_open_button_fg">#777777</color>
<color name="app_banner_card_highlight">#33999999</color>
+
+ <!-- Website Settings Popup colors -->
+ <color name="website_settings_popup_text_link">#5595fe</color>
</resources>
<resources>
<!-- Certificate Viewer Dimensions -->
- <dimen name="certificate_viewer_padding">20dp</dimen>
+ <dimen name="certificate_viewer_padding_wide">24dp</dimen>
+ <dimen name="certificate_viewer_padding_thin">16dp</dimen>
<!-- Accessibility tab switcher -->
<dimen name="accessibility_tab_height">65dp</dimen>
<!-- Custom Menu dimensions -->
<dimen name="menu_width">258dp</dimen>
+ <dimen name="menu_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>
mViews = new ArrayList<LinearLayout>();
mTitles = new ArrayList<String>();
mPadding = (int) context.getResources().getDimension(
- R.dimen.certificate_viewer_padding) / 2;
+ R.dimen.certificate_viewer_padding_wide) / 2;
}
// Show information about an array of DER-encoded data representing a X509 certificate chain.
mVoices = new ArrayList<TtsVoice>();
for (int i = 0; i < locales.length; ++i) {
if (!locales[i].getVariant().isEmpty()) continue;
- if (mTextToSpeech.isLanguageAvailable(locales[i]) > 0) {
- String name = locales[i].getDisplayLanguage();
- if (!locales[i].getCountry().isEmpty()) {
- name += " " + locales[i].getDisplayCountry();
+ try {
+ if (mTextToSpeech.isLanguageAvailable(locales[i]) > 0) {
+ String name = locales[i].getDisplayLanguage();
+ if (!locales[i].getCountry().isEmpty()) {
+ name += " " + locales[i].getDisplayCountry();
+ }
+ TtsVoice voice = new TtsVoice(name, locales[i].toString());
+ mVoices.add(voice);
}
- TtsVoice voice = new TtsVoice(name, locales[i].toString());
- mVoices.add(voice);
+ } catch (java.util.MissingResourceException e) {
+ // Just skip the locale if it's invalid.
}
}
import android.content.Intent;
import android.graphics.Color;
import android.provider.Browser;
-import android.text.Html;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.ViewGroup.LayoutParams;
+import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;
import android.widget.LinearLayout;
public class WebsiteSettingsPopup implements OnClickListener {
private static final String HELP_URL =
"http://www.google.com/support/chrome/bin/answer.py?answer=95617";
+ private static final int DESCRIPTION_TEXT_SIZE_SP = 12;
private final Context mContext;
private final Dialog mDialog;
private final LinearLayout mContainer;
private final WebContents mWebContents;
- private final int mPadding;
+ private final int mPaddingWide, mPaddingThin;
private TextView mCertificateViewer, mMoreInfoLink;
+ private ViewGroup mCertificateLayout, mDescriptionLayout;
private String mLinkUrl;
private WebsiteSettingsPopup(Context context, WebContents webContents) {
mContainer = new LinearLayout(mContext);
mContainer.setOrientation(LinearLayout.VERTICAL);
- mPadding = (int) context.getResources().getDimension(R.dimen.certificate_viewer_padding);
- mContainer.setPadding(mPadding, 0, mPadding, 0);
+ mContainer.setBackgroundColor(Color.WHITE);
+ mPaddingWide = (int) context.getResources().getDimension(
+ R.dimen.certificate_viewer_padding_wide);
+ mPaddingThin = (int) context.getResources().getDimension(
+ R.dimen.certificate_viewer_padding_thin);
+ mContainer.setPadding(mPaddingWide, mPaddingWide + mPaddingThin, mPaddingWide,
+ mPaddingWide);
mDialog = new Dialog(mContext);
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
});
}
- /** Adds a section, which contains an icon, a headline, and a description. */
+ /**
+ * Adds certificate section, which contains an icon, a headline, a
+ * description and a label for certificate info link.
+ */
+ @CalledByNative
+ private void addCertificateSection(int enumeratedIconId, String headline, String description,
+ String label) {
+ View section = addSection(enumeratedIconId, headline, description);
+ assert mCertificateLayout == null;
+ mCertificateLayout = (ViewGroup) section.findViewById(R.id.website_settings_text_layout);
+ if (label != null && !label.isEmpty()) {
+ setCertificateViewer(label);
+ }
+ }
+
+ /**
+ * Adds Description section, which contains an icon, a headline, and a
+ * description. Most likely headline for description is empty
+ */
@CalledByNative
- private void addSection(int enumeratedIconId, String headline, String description) {
+ private void addDescriptionSection(int enumeratedIconId, String headline, String description) {
+ View section = addSection(enumeratedIconId, headline, description);
+ assert mDescriptionLayout == null;
+ mDescriptionLayout = (ViewGroup) section.findViewById(R.id.website_settings_text_layout);
+ }
+
+ private View addSection(int enumeratedIconId, String headline, String description) {
View section = LayoutInflater.from(mContext).inflate(R.layout.website_settings, null);
ImageView i = (ImageView) section.findViewById(R.id.website_settings_icon);
int drawableId = ResourceId.mapToDrawableId(enumeratedIconId);
TextView d = (TextView) section.findViewById(R.id.website_settings_description);
d.setText(description);
+ d.setTextSize(DESCRIPTION_TEXT_SIZE_SP);
if (TextUtils.isEmpty(description)) d.setVisibility(View.GONE);
mContainer.addView(section);
+ return section;
}
- /** Adds a horizontal dividing line to separate sections. */
- @CalledByNative
- private void addDivider() {
- View divider = new View(mContext);
- final int dividerHeight = (int) (2 * mContext.getResources().getDisplayMetrics().density);
- divider.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, dividerHeight));
- divider.setBackgroundColor(Color.GRAY);
- mContainer.addView(divider);
- }
-
- @CalledByNative
private void setCertificateViewer(String label) {
assert mCertificateViewer == null;
- mCertificateViewer = new TextView(mContext);
- mCertificateViewer.setText(Html.fromHtml("<a href='#'>" + label + "</a>"));
+ mCertificateViewer = new TextView(mContext);
+ mCertificateViewer.setText(label);
+ mCertificateViewer.setTextColor(
+ mContext.getResources().getColor(R.color.website_settings_popup_text_link));
+ mCertificateViewer.setTextSize(DESCRIPTION_TEXT_SIZE_SP);
mCertificateViewer.setOnClickListener(this);
- mCertificateViewer.setPadding(0, 0, 0, mPadding);
- mContainer.addView(mCertificateViewer);
+ mCertificateViewer.setPadding(0, mPaddingWide, 0, mPaddingWide);
+ mCertificateLayout.addView(mCertificateViewer);
}
@CalledByNative
private void addUrl(String label, String url) {
mMoreInfoLink = new TextView(mContext);
mLinkUrl = url;
- mMoreInfoLink.setText(Html.fromHtml("<a href='#'>" + label + "</a>"));
- mMoreInfoLink.setPadding(0, mPadding, 0, mPadding);
+ mMoreInfoLink.setText(label);
+ mMoreInfoLink.setTextColor(
+ mContext.getResources().getColor(R.color.website_settings_popup_text_link));
+ mMoreInfoLink.setTextSize(DESCRIPTION_TEXT_SIZE_SP);
+ mMoreInfoLink.setPadding(0, mPaddingWide + mPaddingThin, 0, mPaddingWide);
mMoreInfoLink.setOnClickListener(this);
- mContainer.addView(mMoreInfoLink);
+ mDescriptionLayout.addView(mMoreInfoLink);
}
/** Displays the WebsiteSettingsPopup. */
import android.widget.PopupWindow;
import android.widget.PopupWindow.OnDismissListener;
+import org.chromium.base.SysUtils;
import org.chromium.chrome.R;
import java.util.ArrayList;
private final int mItemRowHeight;
private final int mItemDividerHeight;
private final int mVerticalFadeDistance;
+ private final int mAdditionalVerticalOffset;
private ListPopupWindow mPopup;
private AppMenuAdapter mAdapter;
private AppMenuHandler mHandler;
mItemDividerHeight = itemDividerHeight;
assert mItemDividerHeight >= 0;
+ mAdditionalVerticalOffset =
+ res.getDimensionPixelSize(R.dimen.menu_software_vertical_offset);
mVerticalFadeDistance = res.getDimensionPixelSize(R.dimen.menu_vertical_fade_distance);
}
mPopup.setAnimationStyle(R.style.OverflowMenuAnim);
}
+ // Turn off window animations for low end devices.
+ if (SysUtils.isLowEndDevice()) mPopup.setAnimationStyle(0);
+
Rect bgPadding = new Rect();
mPopup.getBackground().getPadding(bgPadding);
mPopup.getListView().setFadingEdgeLength(mVerticalFadeDistance);
}
- mPopup.getListView().addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- mPopup.getListView().removeOnLayoutChangeListener(this);
- runMenuItemEnterAnimations();
- }
- });
+ // Don't animate the menu items for low end devices.
+ if (!SysUtils.isLowEndDevice()) {
+ mPopup.getListView().addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ mPopup.getListView().removeOnLayoutChangeListener(this);
+ runMenuItemEnterAnimations();
+ }
+ });
+ }
}
private void setPopupOffset(ListPopupWindow popup, int screenRotation, Rect appRect) {
} 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(-anchorHeight);
+ popup.setVerticalOffset(mAdditionalVerticalOffset - anchorHeight);
}
}
}
/**
+ * @return Whether app menu is active. That is, AppMenu is showing or menu button is consuming
+ * touch events to prepare AppMenu showing.
+ */
+ public boolean isAppMenuActive() {
+ return mMenuButton.isPressed() || mMenuHandler.isAppMenuShowing();
+ }
+
+ /**
* Handle the key press event on a menu button.
* @return Whether the app menu was shown as a result of this action.
*/
if (mSeenFirstScrollEvent) return false;
mSeenFirstScrollEvent = true;
- // If the scrolling direction is roughly down on the first onScroll detection,
- // we consider it as dragging start, so shows the app menu. Otherwise, we
- // don't show menu so that toolbar horizontal swiping can happen.
- return -distanceY >= Math.abs(distanceX) && showAppMenu(true);
+ return showAppMenu(true);
}
@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.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.shell.ChromeShellActivity;
+import org.chromium.chrome.shell.ChromeShellTestBase;
+import org.chromium.chrome.shell.R;
+import org.chromium.content.browser.test.util.CallbackHelper;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Tests for the SmartClipProvider.
+ */
+public class SmartClipProviderTest extends ChromeShellTestBase implements Handler.Callback {
+ // This is a key for meta-data in the package manifest. It should NOT
+ // change, as OEMs will use it when they look for the SmartClipProvider
+ // interface.
+ private static final String SMART_CLIP_PROVIDER_KEY =
+ "org.chromium.content.browser.SMART_CLIP_PROVIDER";
+
+ private static class MyCallbackHelper extends CallbackHelper {
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public String getUrl() {
+ return mUrl;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ public String getHtml() {
+ return mHtml;
+ }
+
+ public Rect getRect() {
+ return mRect;
+ }
+
+ public void notifyCalled(String title, String url, String text, String html, Rect rect) {
+ mTitle = title;
+ mUrl = url;
+ mText = text;
+ mHtml = html;
+ mRect = rect;
+ super.notifyCalled();
+ }
+
+ private String mTitle;
+ private String mUrl;
+ private String mText;
+ private String mHtml;
+ private Rect mRect;
+ }
+
+ private ChromeShellActivity mActivity;
+ private MyCallbackHelper mCallbackHelper;
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private Class<?> mSmartClipProviderClass;
+ private Method mSetSmartClipResultHandlerMethod;
+ private Method mExtractSmartClipDataMethod;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mActivity = launchChromeShellWithBlankPage();
+ mCallbackHelper = new MyCallbackHelper();
+ mHandlerThread = new HandlerThread("ContentViewTest thread");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper(), this);
+
+ mSmartClipProviderClass = getSmartClipProviderClass();
+ assertNotNull(mSmartClipProviderClass);
+ mSetSmartClipResultHandlerMethod = mSmartClipProviderClass.getDeclaredMethod(
+ "setSmartClipResultHandler", new Class[] { Handler.class });
+ mExtractSmartClipDataMethod = mSmartClipProviderClass.getDeclaredMethod(
+ "extractSmartClipData",
+ new Class[] { Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE });
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ try {
+ mHandlerThread.quitSafely();
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ // Implements Handler.Callback
+ @Override
+ public boolean handleMessage(Message msg) {
+ Bundle bundle = msg.getData();
+ assertNotNull(bundle);
+ String url = bundle.getString("url");
+ String title = bundle.getString("title");
+ String text = bundle.getString("text");
+ String html = bundle.getString("html");
+ Rect rect = bundle.getParcelable("rect");
+ // We don't care about other values for now.
+ mCallbackHelper.notifyCalled(title, url, text, html, rect);
+ return true;
+ }
+
+ // Create SmartClipProvider interface from package meta-data.
+ private Class<?> getSmartClipProviderClass() throws Exception {
+ ApplicationInfo ai = mActivity.getPackageManager().getApplicationInfo(
+ mActivity.getPackageName(), PackageManager.GET_META_DATA);
+ Bundle bundle = ai.metaData;
+ String className = bundle.getString(SMART_CLIP_PROVIDER_KEY);
+ assertNotNull(className);
+ return Class.forName(className);
+ }
+
+ // Returns the first smart clip provider under the root view using DFS.
+ private Object findSmartClipProvider(View v) {
+ if (mSmartClipProviderClass.isInstance(v)) {
+ return v;
+ } else if (v instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) v;
+ int count = viewGroup.getChildCount();
+ for (int i = 0; i < count; ++i) {
+ View c = viewGroup.getChildAt(i);
+ Object found = findSmartClipProvider(c);
+ if (found != null)
+ return found;
+ }
+ }
+ return null;
+ }
+
+ @MediumTest
+ @Feature({"SmartClip"})
+ public void testSmartClipDataCallback() throws InterruptedException, TimeoutException {
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+ @Override
+ public void run() {
+ // This emulates what OEM will be doing when they want to call
+ // functions on SmartClipProvider through view hierarchy.
+
+ // Implementation of SmartClipProvider such as ContentView or
+ // JellyBeanContentView can be found somewhere under content_container.
+ Object scp = findSmartClipProvider(
+ getActivity().findViewById(R.id.content_container));
+ assertNotNull(scp);
+ try {
+ mSetSmartClipResultHandlerMethod.invoke(scp, mHandler);
+ mExtractSmartClipDataMethod.invoke(scp, 10, 20, 100, 70);
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail();
+ }
+ }
+ });
+ mCallbackHelper.waitForCallback(0, 1); // call count: 0 --> 1
+ assertEquals("about:blank", mCallbackHelper.getTitle());
+ assertEquals("about:blank", mCallbackHelper.getUrl());
+ assertNotNull(mCallbackHelper.getText());
+ assertNotNull(mCallbackHelper.getHtml());
+ assertNotNull(mCallbackHelper.getRect());
+ }
+}
<action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
</intent-filter>
</receiver>
+ <meta-data android:name="org.chromium.content.browser.SMART_CLIP_PROVIDER"
+ android:value="org.chromium.content.browser.SmartClipProvider" />
+
</application>
</manifest>
<translation id="7347751611463936647">لاستخدام هذه الإضافة، اكتب "<ph name="EXTENSION_KEYWORD"/>"، ثم TAB، ثم الأمر أو البحث.</translation>
<translation id="878431691778285679">يبدو أنك تدير مستخدمًا من خلال هذا الاسم فعلاً.<ph name="LINE_BREAK"/>هل كنت تريد <ph name="BEGIN_LINK"/>الاستيراد <ph name="PROFILE_NAME"/> إلى هذا الجهاز<ph name="END_LINK"/>؟</translation>
<translation id="2912905526406334195">يريد <ph name="HOST"/> استخدام الميكروفون.</translation>
-<translation id="2805756323405976993">اÙ\84تطبÙ\8aÙ\82ات</translation>
+<translation id="2805756323405976993">تطبيقات</translation>
<translation id="5151511998946489774">تم التحقق من هوية موقع الويب هذا بواسطة <ph name="ISSUER"/> وهو قابل للتدقيق بشكل عام.</translation>
<translation id="1608626060424371292">إزالة هذا المستخدم</translation>
<translation id="2779552785085366231">يمكن إضافة هذه الصفحة إلى تطبيقات Launcher</translation>
<translation id="7347751611463936647">Upang gamitin ang extension na ito, i-type ang "<ph name="EXTENSION_KEYWORD"/>", pagkatapos ay ang TAB, pagkatapos ay ang iyong command o paghahanap.</translation>
<translation id="878431691778285679">Mukhang pinamamahalaan mo na ang isang user sa pangalang iyan.<ph name="LINE_BREAK"/>Gusto mo bang <ph name="BEGIN_LINK"/>i-import si <ph name="PROFILE_NAME"/> sa device na ito<ph name="END_LINK"/>?</translation>
<translation id="2912905526406334195">Gustong gamitin ng <ph name="HOST"/> ang iyong microphone.</translation>
-<translation id="2805756323405976993">Apps</translation>
+<translation id="2805756323405976993">Mga App</translation>
<translation id="5151511998946489774">Ang pagkakakilanlan ng website na ito ay na-verify ni <ph name="ISSUER"/> at na-o-audit ng publiko.</translation>
<translation id="1608626060424371292">Alisin ang user na ito</translation>
<translation id="2779552785085366231">Maaaring idagdag ang page na ito sa App Launcher</translation>
<translation id="7347751611463936647">Pour utiliser cette extension, saisissez "<ph name="EXTENSION_KEYWORD"/>", TAB, puis votre commande ou votre recherche.</translation>
<translation id="878431691778285679">Il semble que vous gérez déjà un utilisateur portant ce nom.<ph name="LINE_BREAK"/>Voulez-vous <ph name="BEGIN_LINK"/>importer <ph name="PROFILE_NAME"/> sur cet appareil<ph name="END_LINK"/> ?</translation>
<translation id="2912905526406334195"><ph name="HOST"/> veut utiliser votre micro.</translation>
-<translation id="2805756323405976993">Google Apps</translation>
+<translation id="2805756323405976993">Applications</translation>
<translation id="5151511998946489774">L'identité de ce site Web a été validée par <ph name="ISSUER"/>, et elle est vérifiable publiquement.</translation>
<translation id="1608626060424371292">Supprimer cet utilisateur</translation>
<translation id="2779552785085366231">Cette page peut être ajoutée au lanceur d'applications.</translation>
<translation id="7347751611463936647">આ એક્સ્ટેંશનનો ઉપયોગ કરવા, "<ph name="EXTENSION_KEYWORD"/>" ટાઇપ કરો, તે પછી TAB, તે પછી તમારો આદેશ અથવા શોધ.</translation>
<translation id="878431691778285679">એવું લાગે છે કે તમે પહેલાંથી જ તે નામના વપરાશકર્તાનું સંચાલન કરી રહ્યાં છો.<ph name="LINE_BREAK"/>શું તમે <ph name="BEGIN_LINK"/>આ ઉપકરણ પર <ph name="PROFILE_NAME"/> આયાત કરવા માંગો છો<ph name="END_LINK"/>?</translation>
<translation id="2912905526406334195"><ph name="HOST"/> તમારા માઇક્રોફોનનો ઉપયોગ કરવા માગે છે.</translation>
-<translation id="2805756323405976993">àª\8fપà«\8dલિàª\95à«\87શનà«\8dસ</translation>
+<translation id="2805756323405976993">એપ્સ</translation>
<translation id="5151511998946489774"><ph name="ISSUER"/> દ્વારા આ વેબસાઇટની ઓળખ ચકાસવામાં આવી છે અને તે સાર્વજનિક રૂપે ઓડિટેબલ છે.</translation>
<translation id="1608626060424371292">આ વપરાશકર્તાને દૂર કરો</translation>
<translation id="2779552785085366231">આ પૃષ્ઠ એપ લૉન્ચરમાં ઉમેરી શકાય છે</translation>
<translation id="7347751611463936647">Da biste upotrijebili to proširenje, upišite "<ph name="EXTENSION_KEYWORD"/>", zatim pritisnite TAB, a zatim svoju naredbu ili pretraživanje.</translation>
<translation id="878431691778285679">Izgleda da već upravljate korisnikom pod tim imenom.<ph name="LINE_BREAK"/>Jeste li htjeli <ph name="BEGIN_LINK"/>uvesti korisnika <ph name="PROFILE_NAME"/> na ovaj uređaj<ph name="END_LINK"/>?</translation>
<translation id="2912905526406334195">Host <ph name="HOST"/> želi upotrijebiti vaš mikrofon.</translation>
-<translation id="2805756323405976993">Apps</translation>
+<translation id="2805756323405976993">Aplikacije</translation>
<translation id="5151511998946489774">Identitet te web-lokacije potvrdio je izdavač <ph name="ISSUER"/> i može se javno nadzirati.</translation>
<translation id="1608626060424371292">Ukloni tog korisnika</translation>
<translation id="2779552785085366231">Tu stranicu možete dodati u Pokretač aplikacija</translation>
<translation id="7347751611463936647">Untuk menggunakan ekstensi ini, ketik "<ph name="EXTENSION_KEYWORD"/>", lalu TAB, kemudian perintah atau penelusuran Anda.</translation>
<translation id="878431691778285679">Tampaknya Anda telah mengelola pengguna dengan nama tersebut.<ph name="LINE_BREAK"/>Apakah Anda ingin <ph name="BEGIN_LINK"/>mengimpor <ph name="PROFILE_NAME"/> ke perangkat ini<ph name="END_LINK"/>?</translation>
<translation id="2912905526406334195"><ph name="HOST"/> ingin menggunakan mikrofon Anda.</translation>
-<translation id="2805756323405976993">Apps</translation>
+<translation id="2805756323405976993">Apl</translation>
<translation id="5151511998946489774">Identitas situs web ini telah diverifikasi oleh <ph name="ISSUER"/> dan dapat diaudit secara publik.</translation>
<translation id="1608626060424371292">Hapus pengguna ini</translation>
<translation id="2779552785085366231">Laman ini dapat ditambahkan ke Peluncur Aplikasi</translation>
<translation id="8353683614194668312">Aplikasi/ekstensi dapat:</translation>
<translation id="1047956942837015229">Menghapus <ph name="COUNT"/> item...</translation>
<translation id="1531961661616401172">Aktifkan Pemberitahuan Tersinkron eksperimental.</translation>
-<translation id="7361039089383199231">$1 byte</translation>
+<translation id="7361039089383199231">$1 bita</translation>
<translation id="191688485499383649">Terjadi kesalahan tidak dikenal saat mencoba menyambung ke "<ph name="DEVICE_NAME"/>".</translation>
<translation id="7208594729785140450">Google Wallet tidak mendukung versi Chrome ini atau tidak mengenali kunci API Google Anda.</translation>
<translation id="6874681241562738119">Kesalahan Masuk</translation>
<translation id="4084682180776658562">Bookmark</translation>
<translation id="8859057652521303089">Pilih bahasa Anda:</translation>
<translation id="2632795170092344386">Hapus cookies serta data situs dan plugin lainnya saat Anda menutup browser</translation>
-<translation id="5941864346249299673">Jumlah byte yang terbaca di seluruh jaringan</translation>
+<translation id="5941864346249299673">Jumlah bita yang terbaca di seluruh jaringan</translation>
<translation id="3030138564564344289">Coba ulang unduhan</translation>
<translation id="2603463522847370204">Buka di &jendela penyamaran</translation>
<translation id="2951236788251446349">Ubur-ubur</translation>
<translation id="7347751611463936647">Per utilizzare questa estensione, digita "<ph name="EXTENSION_KEYWORD"/>", quindi TAB, poi il tuo comando o la tua ricerca.</translation>
<translation id="878431691778285679">Sembra che tu gestisca già un utente con il nome indicato.<ph name="LINE_BREAK"/>Volevi <ph name="BEGIN_LINK"/>importarlo<ph name="PROFILE_NAME"/> in questo dispositivo<ph name="END_LINK"/>?</translation>
<translation id="2912905526406334195"><ph name="HOST"/> vuole utilizzare il microfono.</translation>
-<translation id="2805756323405976993">Google Apps</translation>
+<translation id="2805756323405976993">App</translation>
<translation id="5151511998946489774">L'identità di questo sito web è stata verificata da <ph name="ISSUER"/> e può essere controllata pubblicamente.</translation>
<translation id="1608626060424371292">Rimuovi questo utente</translation>
<translation id="2779552785085366231">Questa pagina può essere aggiunta ad Avvio applicazioni</translation>
<translation id="7347751611463936647">כדי להשתמש בתוסף זה, הקלד "<ph name="EXTENSION_KEYWORD"/>", לאחר מכן הקש TAB ולאחר מכן הקלד את הפקודה או מונח החיפוש.</translation>
<translation id="878431691778285679">נראה שאתה כבר מנהל משתמש עם השם הזה.<ph name="LINE_BREAK"/>האם רצית <ph name="BEGIN_LINK"/>לייבא את <ph name="PROFILE_NAME"/> אל המכשיר הזה<ph name="END_LINK"/>?</translation>
<translation id="2912905526406334195"><ph name="HOST"/> מעוניין להשתמש במיקרופון שלך.</translation>
-<translation id="2805756323405976993">Apps</translation>
+<translation id="2805756323405976993">אפליקציות</translation>
<translation id="5151511998946489774">הזהות של האתר הזה אומתה על ידי <ph name="ISSUER"/>, והוא זמין לביקורת ציבורית.</translation>
<translation id="1608626060424371292">הסר את המשתמש הזה</translation>
<translation id="2779552785085366231">ניתן להוסיף דף זה למפעיל היישומים</translation>
<translation id="7347751611463936647">ಈ ವಿಸ್ತರಣೆಯನ್ನು ಬಳಸಲು, "<ph name="EXTENSION_KEYWORD"/>" ಅನ್ನು ಟೈಪ್ ಮಾಡಿ, ನಂತರ TAB, ನಂತರ ನಿಮ್ಮ ಆದೇಶ ಅಥವಾ ಹುಡುಕಾಟವನ್ನು ಟೈಪ್ ಮಾಡಿ.</translation>
<translation id="878431691778285679">ಆ ಹೆಸರಿನ ಮೂಲಕ ನೀವು ಈಗಾಗಲೇ ಬಳಕೆದಾರರನ್ನು ನಿರ್ವಹಿಸುತ್ತಿರುವಂತೆ ತೋರುತ್ತಿದೆ.<ph name="LINE_BREAK"/>ನೀವು ಈ ಸಾಧನಕ್ಕೆ <ph name="BEGIN_LINK"/>ಆಮದು ಮಾಡಲು <ph name="PROFILE_NAME"/> ಬಯಸುವಿರಾ<ph name="END_LINK"/>?</translation>
<translation id="2912905526406334195">ನಿಮ್ಮ ಮೈಕ್ರೊಫೋನ್ ಅನ್ನು <ph name="HOST"/> ಬಳಸಬೇಕೆಂದು ಬಯಸುತ್ತದೆ.</translation>
-<translation id="2805756323405976993">Apps</translation>
+<translation id="2805756323405976993">ಆಪ್ಸ್</translation>
<translation id="5151511998946489774">ಈ ವೆಬ್ಸೈಟ್ ಗುರುತನ್ನು <ph name="ISSUER"/> ಮೂಲಕ ಪರಿಶೀಲಿಸಲಾಗಿದೆ ಮತ್ತು ಇದನ್ನು ಸಾರ್ವಜನಿಕವಾಗಿ ಪರಿಶೋಧಿಸಬಹುದಾಗಿದೆ.</translation>
<translation id="1608626060424371292">ಈ ಬಳಕೆದಾರರನ್ನು ತೆಗೆದುಹಾಕಿ</translation>
<translation id="2779552785085366231">ಈ ಪುಟವನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಲಾಂಚರ್ಗೆ ಸೇರಿಸಬಹುದಾಗಿದೆ</translation>
<translation id="7347751611463936647">이 확장 프로그램을 사용하려면 '<ph name="EXTENSION_KEYWORD"/>'(을)를 입력하고 TAB을 누른 다음 명령어 또는 검색어를 입력하세요.</translation>
<translation id="878431691778285679">이미 해당 이름으로 사용자를 관리 중인 것 같습니다.<ph name="LINE_BREAK"/><ph name="BEGIN_LINK"/><ph name="PROFILE_NAME"/>을(를) 이 기기로 가져오시겠습니까<ph name="END_LINK"/>?</translation>
<translation id="2912905526406334195"><ph name="HOST"/>에서 사용자의 마이크를 사용하려고 합니다.</translation>
-<translation id="2805756323405976993">ì\9d\91ì\9a©í\94\84ë¡\9cê·¸ë\9e¨</translation>
+<translation id="2805756323405976993">ì\95±</translation>
<translation id="5151511998946489774">이 웹사이트의 ID는 <ph name="ISSUER"/>에 의해 확인되었고 공개 감사가 가능합니다.</translation>
<translation id="1608626060424371292">이 사용자 삭제</translation>
<translation id="2779552785085366231">이 페이지를 앱 런처에 추가할 수 있습니다.</translation>
<translation id="7347751611463936647">Als je deze extensie wilt gebruiken, typ je '<ph name="EXTENSION_KEYWORD"/>', druk je op Tab en geef je je opdracht of zoekopdracht op.</translation>
<translation id="878431691778285679">Het lijkt erop dat je al een gebruiker met die naam beheert.<ph name="LINE_BREAK"/>Wil je <ph name="BEGIN_LINK"/><ph name="PROFILE_NAME"/> importeren naar dit apparaat<ph name="END_LINK"/>?</translation>
<translation id="2912905526406334195"><ph name="HOST"/> wil gebruikmaken van je microfoon.</translation>
-<translation id="2805756323405976993">Google Apps</translation>
+<translation id="2805756323405976993">Apps</translation>
<translation id="5151511998946489774">De identiteit van deze website is geverifieerd door <ph name="ISSUER"/> en is openbaar controleerbaar.</translation>
<translation id="1608626060424371292">Deze gebruiker verwijderen</translation>
<translation id="2779552785085366231">Deze pagina kan worden toegevoegd aan de App Launcher</translation>
<translation id="2220529011494928058">Пријави проблем</translation>
<translation id="8475107630436748034">Експеримент са TLS/SSL упозорењем</translation>
<translation id="7857823885309308051">Ово може да потраје један минут...</translation>
-<translation id="370665806235115550">Учитавање...</translation>
+<translation id="370665806235115550">Учитава се...</translation>
<translation id="2580924999637585241">Укупно: <ph name="NUMBER_OF_SHEETS"/><ph name="SHEETS_LABEL"/></translation>
<translation id="3810973564298564668">Промени</translation>
<translation id="254416073296957292">&Подешавања језика...</translation>
<translation id="1556537182262721003">சுயவிவரத்தில் நீட்டிப்புக் கோப்பகத்தை நகர்த்த முடியவில்லை.</translation>
<translation id="2946640296642327832">புளூடூத்தை இயக்கு</translation>
<translation id="5866557323934807206">எதிர்கால வருகைகளுக்கு இந்த அமைப்புகளை அழி</translation>
-<translation id="126710816202626562">à®®à¯\86ாழிபà¯\86யரà¯\8dபà¯\8dபிறà¯\8dà®\95ான à®®à¯\86ாழி:</translation>
+<translation id="126710816202626562">à®®à¯\8aழிபà¯\86யரà¯\8dபà¯\8dபிறà¯\8dà®\95ான à®®à¯\8aழி:</translation>
<translation id="1194381338562257973">சோதனை முறையிலான குறுகியகாலப் பயன்பாடுகளை இயக்கு.</translation>
<translation id="5355351445385646029">பிரதிநிதியைத் தேர்ந்தெடுக்க Space ஐ அழுத்துக</translation>
<translation id="5453029940327926427">தாவல்களை மூடுக</translation>
<translation id="7211828883345145708">Chromium ஐப் பிழைத்திருத்துவதற்குப் பயன்படும் கூடுதல் விசைப்பலகைக் குறுக்குவழிகளை இயக்கும்.</translation>
<translation id="8319414634934645341">நீட்டிக்கப்பட்ட விசைப் பயன்பாடு</translation>
<translation id="6056710589053485679">இயல்பாக மீண்டும் ஏற்று</translation>
-<translation id="4563210852471260509">தà¯\86ாà®\9fà®\95à¯\8dà®\95 à®\89ளà¯\8dளà¯\80à®\9fà¯\8dà®\9fà¯\81 à®®à¯\86ாழி சீனம்</translation>
+<translation id="4563210852471260509">தà¯\86ாà®\9fà®\95à¯\8dà®\95 à®\89ளà¯\8dளà¯\80à®\9fà¯\8dà®\9fà¯\81 à®®à¯\8aழி சீனம்</translation>
<translation id="2888807692577297075"><b>"<ph name="SEARCH_STRING"/>"</b> உடன் பொருந்தும் உருப்படிகள் எதுவுமில்லை</translation>
<translation id="3908501907586732282">நீட்டிப்பை இயக்கு</translation>
<translation id="6897140037006041989">பயனர் முகவர்</translation>
<translation id="1903219944620007795">உரை உள்ளீட்டுக்கு, ஒரு மொழியைத் தேர்ந்தெடுத்து கிடைக்கும் உள்ளீட்டு முறைகளைக் காண்க.</translation>
<translation id="1850508293116537636">&வலஞ்சுழியாகச் சுற்று</translation>
<translation id="76113267417806263">ஆம், உறுதியாக உள்ளேன்</translation>
-<translation id="7209475358897642338">à®\89à®\99à¯\8dà®\95ளà¯\8d à®®à¯\86ாழி என்ன?</translation>
+<translation id="7209475358897642338">à®\89à®\99à¯\8dà®\95ளà¯\8d à®®à¯\8aழி என்ன?</translation>
<translation id="140520891692800925"><ph name="PROFILE_DISPLAY_NAME"/> (கண்காணிக்கப்படும் பயனர்)</translation>
<translation id="9149866541089851383">மாற்று...</translation>
<translation id="8735794438432839558">உங்கள் Chromebook இல் உள்நுழைய, இணையத்துடன் இணையவும்.</translation>
<translation id="4278390842282768270">允許</translation>
<translation id="2074527029802029717">取消分頁固定</translation>
<translation id="1533897085022183721">電池可使用時間低於 <ph name="MINUTES"/>。</translation>
-<translation id="7382160026931194400">已儲存的內容設定及搜尋引擎不會遭到清除,而這可能反映您的瀏覽習慣。</translation>
+<translation id="7382160026931194400">已儲存的|內容設定|和#搜尋引擎#資料不會被清除,而且可能會反映您的瀏覽習慣。</translation>
<translation id="7503821294401948377">無法載入瀏覽器動作的圖示「<ph name="ICON"/>」。</translation>
<translation id="4809190954660909198">新增帳單詳細資訊...</translation>
<translation id="3942946088478181888">我需要進一步資訊</translation>
<translation id="7943385054491506837">Colemak 美式配置</translation>
<translation id="8203365863660628138">確認安裝</translation>
<translation id="2533972581508214006">回報錯誤的警告</translation>
-</translationbundle>
\ No newline at end of file
+</translationbundle>
num_sites,
num_sites + 1,
base::Histogram::kUmaTargetedHistogramFlag);
- counter->Add(position);
+ if (counter)
+ counter->Add(position);
}
} // namespace
case STATUS_ERROR:
delegate_->OnKioskAppDataLoadFailure(app_id_);
break;
- };
+ }
}
net::URLRequestContextGetter* KioskAppData::GetRequestContextGetter() {
GetRequestContextGetter(),
GURL(),
app_id_));
+ webstore_fetcher_->set_max_auto_retries(3);
webstore_fetcher_->Start();
}
app_data->LoadFromInstalledApp(profile, app);
}
+void KioskAppManager::RetryFailedAppDataFetch() {
+ for (size_t i = 0; i < apps_.size(); ++i) {
+ if (apps_[i]->status() == KioskAppData::STATUS_ERROR)
+ apps_[i]->Load();
+ }
+}
+
void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) {
observers_.AddObserver(observer);
}
prefs->Set(apps_[i]->app_id(), new base::DictionaryValue);
external_cache_->UpdateExtensionsList(prefs.Pass());
+ RetryFailedAppDataFetch();
+
FOR_EACH_OBSERVER(KioskAppManagerObserver, observers_,
OnKioskAppsSettingsChanged());
}
Profile* profile,
const extensions::Extension* app);
+ void RetryFailedAppDataFetch();
+
void AddObserver(KioskAppManagerObserver* observer);
void RemoveObserver(KioskAppManagerObserver* observer);
#include "ui/base/ime/chromeos/ime_keymap.h"
#include "ui/events/event.h"
#include "ui/events/event_processor.h"
+#include "ui/events/keycodes/dom4/keycode_converter.h"
#include "ui/keyboard/keyboard_controller.h"
#include "ui/keyboard/keyboard_util.h"
if (code == "Escape")
return "Esc";
if (code == "Backspace" || code == "Tab" ||
- code == "Enter" || code == "CapsLock")
+ code == "Enter" || code == "CapsLock" ||
+ code == "Power")
return code;
+ // Cases for media keys.
+ switch (event.key_code()) {
+ case ui::VKEY_BROWSER_BACK:
+ case ui::VKEY_F1:
+ return "HistoryBack";
+ case ui::VKEY_BROWSER_FORWARD:
+ case ui::VKEY_F2:
+ return "HistoryForward";
+ case ui::VKEY_BROWSER_REFRESH:
+ case ui::VKEY_F3:
+ return "BrowserRefresh";
+ case ui::VKEY_MEDIA_LAUNCH_APP2:
+ case ui::VKEY_F4:
+ return "ChromeOSFullscreen";
+ case ui::VKEY_MEDIA_LAUNCH_APP1:
+ case ui::VKEY_F5:
+ return "ChromeOSSwitchWindow";
+ case ui::VKEY_BRIGHTNESS_DOWN:
+ case ui::VKEY_F6:
+ return "BrightnessDown";
+ case ui::VKEY_BRIGHTNESS_UP:
+ case ui::VKEY_F7:
+ return "BrightnessUp";
+ case ui::VKEY_VOLUME_MUTE:
+ case ui::VKEY_F8:
+ return "AudioVolumeMute";
+ case ui::VKEY_VOLUME_DOWN:
+ case ui::VKEY_F9:
+ return "AudioVolumeDown";
+ case ui::VKEY_VOLUME_UP:
+ case ui::VKEY_F10:
+ return "AudioVolumeUp";
+ default:
+ break;
+ }
uint16 ch = 0;
// Ctrl+? cases, gets key value for Ctrl is not down.
if (event.flags() & ui::EF_CONTROL_DOWN) {
DCHECK(ext_event);
ext_event->type = (event.type() == ui::ET_KEY_RELEASED) ? "keyup" : "keydown";
- ext_event->code = event.code();
+ std::string dom_code = event.code();
+ if (dom_code ==
+ ui::KeycodeConverter::GetInstance()->InvalidKeyboardEventCode())
+ dom_code = ui::KeyboardCodeToDomKeycode(event.key_code());
+ ext_event->code = dom_code;
ext_event->key_code = static_cast<int>(event.key_code());
ext_event->alt_key = event.IsAltDown();
ext_event->ctrl_key = event.IsControlDown();
#include "ash/ime/input_method_menu_item.h"
#include "ash/ime/input_method_menu_manager.h"
#include "base/bind_helpers.h"
+#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_test_message_listener.h"
EXPECT_TRUE(keyevent_listener.was_satisfied());
callback.WaitUntilCalled();
}
+ // Media keys cases.
+ const struct {
+ ui::KeyboardCode keycode;
+ const char* code;
+ const char* key;
+ } kMediaKeyCases[] = {
+ { ui::VKEY_BROWSER_BACK, "BrowserBack", "HistoryBack" },
+ { ui::VKEY_BROWSER_FORWARD, "BrowserForward", "HistoryForward" },
+ { ui::VKEY_BROWSER_REFRESH, "BrowserRefresh", "BrowserRefresh" },
+ { ui::VKEY_MEDIA_LAUNCH_APP2, "ChromeOSFullscreen", "ChromeOSFullscreen" },
+ { ui::VKEY_MEDIA_LAUNCH_APP1,
+ "ChromeOSSwitchWindow", "ChromeOSSwitchWindow" },
+ { ui::VKEY_BRIGHTNESS_DOWN, "BrightnessDown", "BrightnessDown" },
+ { ui::VKEY_BRIGHTNESS_UP, "BrightnessUp", "BrightnessUp" },
+ { ui::VKEY_VOLUME_MUTE, "VolumeMute", "AudioVolumeMute" },
+ { ui::VKEY_VOLUME_DOWN, "VolumeDown", "AudioVolumeDown" },
+ { ui::VKEY_VOLUME_UP, "VolumeUp", "AudioVolumeUp" },
+ { ui::VKEY_F1, "F1", "HistoryBack" },
+ { ui::VKEY_F2, "F2", "HistoryForward" },
+ { ui::VKEY_F3, "F3", "BrowserRefresh" },
+ { ui::VKEY_F4, "F4", "ChromeOSFullscreen" },
+ { ui::VKEY_F5, "F5", "ChromeOSSwitchWindow" },
+ { ui::VKEY_F6, "F6", "BrightnessDown" },
+ { ui::VKEY_F7, "F7", "BrightnessUp" },
+ { ui::VKEY_F8, "F8", "AudioVolumeMute" },
+ { ui::VKEY_F9, "F9", "AudioVolumeDown" },
+ { ui::VKEY_F10, "F10", "AudioVolumeUp" },
+ };
+ for (size_t i = 0; i < arraysize(kMediaKeyCases); ++i) {
+ SCOPED_TRACE(std::string("KeyDown, ") + kMediaKeyCases[i].code);
+ KeyEventDoneCallback callback(false);
+ const std::string expected_value =
+ base::StringPrintf("onKeyEvent::keydown:%s:%s:false:false:false:false",
+ kMediaKeyCases[i].key, kMediaKeyCases[i].code);
+ ExtensionTestMessageListener keyevent_listener(expected_value, false);
+
+ ui::KeyEvent key_event(ui::ET_KEY_PRESSED,
+ kMediaKeyCases[i].keycode,
+ kMediaKeyCases[i].code,
+ ui::EF_NONE);
+ engine_handler->ProcessKeyEvent(key_event,
+ base::Bind(&KeyEventDoneCallback::Run,
+ base::Unretained(&callback)));
+ ASSERT_TRUE(keyevent_listener.WaitUntilSatisfied());
+ EXPECT_TRUE(keyevent_listener.was_satisfied());
+ callback.WaitUntilCalled();
+ }
// TODO(nona): Add browser tests for other API as well.
{
SCOPED_TRACE("commitText test");
#include "base/observer_list.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
+#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
// Make sure that system tray is enabled after this flow.
ash::Shell::GetInstance()->GetPrimarySystemTray()->SetEnabled(true);
display_host_->Finalize();
+
+ // Reset wallpaper if cancel adding user from multiple user sign in page.
+ if (UserManager::Get()->IsUserLoggedIn()) {
+ WallpaperManager::Get()->SetUserWallpaperDelayed(
+ UserManager::Get()->GetActiveUser()->email());
+ }
}
bool UserAddingScreenImpl::IsRunning() {
if (line.compare(0, 3, "cpu") != 0)
continue;
+ // The number of entries in /proc/stat may mismatch the size of infos
+ // because the number of online processors may change after the value has
+ // been decided in CpuInfoProvider::QueryInfo().
+ //
+ // TODO(jchuang): fix the fail case by using the number of configured
+ // processors instead of online processors.
+ if (i == infos->size()) {
+ LOG(ERROR) << "Got more entries in /proc/stat than online CPUs";
+ return false;
+ }
+
uint64 user = 0, nice = 0, sys = 0, idle = 0;
int vals = sscanf(line.c_str(), "%*s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64,
&user, &nice, &sys, &idle);
DCHECK_EQ(4, vals);
- DCHECK(i < infos->size());
infos->at(i)->usage.kernel = static_cast<double>(sys);
infos->at(i)->usage.user = static_cast<double>(user + nice);
infos->at(i)->usage.idle = static_cast<double>(idle);
infos->at(i)->usage.total = static_cast<double>(sys + user + nice + idle);
++i;
}
- DCHECK_EQ(infos->size(), i);
+ if (i < infos->size()) {
+ LOG(ERROR) << "Got fewer entries in /proc/stat than online CPUs";
+ return false;
+ }
+
return true;
}
: delegate_(delegate),
request_context_(request_context),
referrer_url_(referrer_url),
- id_(webstore_item_id) {
+ id_(webstore_item_id),
+ max_auto_retries_(0) {
}
WebstoreDataFetcher::~WebstoreDataFetcher() {}
webstore_data_url_fetcher_->SetReferrer(referrer_url_.spec());
webstore_data_url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
net::LOAD_DISABLE_CACHE);
+ if (max_auto_retries_ > 0) {
+ webstore_data_url_fetcher_->SetMaxRetriesOn5xx(max_auto_retries_);
+ webstore_data_url_fetcher_->SetAutomaticallyRetryOnNetworkChanges(
+ max_auto_retries_);
+ }
webstore_data_url_fetcher_->Start();
}
void Start();
+ void set_max_auto_retries(int max_retries) {
+ max_auto_retries_ = max_retries;
+ }
+
private:
void OnJsonParseSuccess(scoped_ptr<base::Value> parsed_json);
void OnJsonParseFailure(const std::string& error);
// For fetching webstore JSON data.
scoped_ptr<net::URLFetcher> webstore_data_url_fetcher_;
+ // Maximum auto retry times on server 5xx error or ERR_NETWORK_CHANGED.
+ // Default is 0 which means to use the URLFetcher default behavior.
+ int max_auto_retries_;
+
DISALLOW_COPY_AND_ASSIGN(WebstoreDataFetcher);
};
row != details->rows.end(); ++row)
needs_to_be_cached_ |= private_data_->DeleteURL(row->url());
}
+ // If we made changes, destroy the previous cache. Otherwise, if we go
+ // through an unclean shutdown (and therefore fail to write a new cache file),
+ // when Chrome restarts and we restore from the previous cache, we'll end up
+ // searching over URLs that may be deleted. This would be wrong, and
+ // surprising to the user who bothered to delete some URLs from his/her
+ // history. In this situation, deleting the cache is a better solution than
+ // writing a new cache (after deleting the URLs from the in-memory structure)
+ // because deleting the cache forces it to be rebuilt from history upon
+ // startup. If we instead write a new, updated cache then at the time of next
+ // startup (after an unclean shutdown) we will not rebuild the in-memory data
+ // structures from history but rather use the cache. This solution is
+ // mediocre because this cache may not have the most-recently-visited URLs
+ // in it (URLs visited after user deleted some URLs from history), which
+ // would be odd and confusing. It's better to force a rebuild.
+ base::FilePath path;
+ if (needs_to_be_cached_ && GetCacheFilePath(&path)) {
+ content::BrowserThread::PostBlockingPoolTask(
+ FROM_HERE, base::Bind(DeleteCacheFile, path));
+ }
}
// Restoring from Cache --------------------------------------------------------
// to be cached. Set to false when the index has been cached. Used as a
// temporary safety check to insure that the cache is saved before the
// index has been destructed.
- // TODO(mrossetti): Eliminate once the transition to SQLite has been done.
- // http://crbug.com/83659
bool needs_to_be_cached_;
DISALLOW_COPY_AND_ASSIGN(InMemoryURLIndex);
void ProfileInfoCache::DownloadHighResAvatar(
size_t icon_index,
const base::FilePath& profile_path) {
+ // Downloading is only supported on desktop.
+#if defined(OS_ANDROID) || defined(OS_IOS) || defined(OS_CHROMEOS)
+ return;
+#endif
+
// TODO(noms): We should check whether the file already exists on disk
// before trying to re-download it. For now, since this is behind a flag and
// the resources are still changing, re-download it every time the profile
ASSERT_FALSE(names[i].empty());
}
+// High res avatar downloading is only supported on desktop.
+#if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS)
TEST_F(ProfileInfoCacheTest, DownloadHighResAvatarTest) {
EXPECT_EQ(0U, GetCache()->GetNumberOfProfiles());
base::FilePath path_1 = GetProfilePath("path_1");
EXPECT_TRUE(base::DeleteFile(icon_path, true));
EXPECT_FALSE(base::PathExists(icon_path));
}
+#endif
* @return {boolean} True if the default action should be performed.
*/
cvox.ChromeVoxEventWatcher.mouseOverEventWatcher = function(evt) {
- var hasTouch = 'ontouchstart' in window;
+ // Chrome simulates the meta key for mouse events generated from
+ // touch exploration.
+ var isTouchEvent = (evt.metaKey);
+
var mouseoverDelayMs = cvox.ChromeVoxEventWatcher.mouseoverDelayMs;
- if (hasTouch) {
+ if (isTouchEvent) {
mouseoverDelayMs = 0;
} else if (!cvox.ChromeVoxEventWatcher.focusFollowsMouse) {
return true;
}
#hid-detection #hid-keyboard-pincode {
- -webkit-padding-start: 40px;
+ -webkit-padding-start: 60px;
padding-bottom: 15px;
padding-top: 15px;
}
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDagiQy1VGkO2CHJSjVh7eU5GtuBuOlg2/cTZt7203AcevqpcDd+65S2/yd9KAELYcU6pK8nHVGYBMI6s0u+0RgXfIJ0eFOlTlgfAQWHvg8ovHtJlFJd1COrOkbntD9+s9Jobr3ldmow87aZF1bVHUY4khVP56cZe6adlVw2wK31QIDAQAB",
"name": "hotword helper",
- "version": "0.0.1.0",
+ "version": "0.0.2.0",
"manifest_version": 2,
"background": {
settings_service,
SupervisedUserSharedSettingsServiceFactory::GetForBrowserContext(
profile_),
- pref_service->GetString(prefs::kProfileName),
+ GetSupervisedUserName(),
pref_service->GetString(prefs::kSupervisedUserId)));
}
is_profile_active_ = profile_became_active;
}
+
+std::string SupervisedUserService::GetSupervisedUserName() const {
+#if defined(OS_CHROMEOS)
+ // The active user can be NULL in unit tests.
+ if (chromeos::UserManager::Get()->GetActiveUser()) {
+ return UTF16ToUTF8(chromeos::UserManager::Get()->GetUserDisplayName(
+ chromeos::UserManager::Get()->GetActiveUser()->GetUserID()));
+ }
+ return std::string();
+#else
+ return profile_->GetPrefs()->GetString(prefs::kProfileName);
+#endif
+}
// corresponding preference is changed.
void UpdateManualURLs();
+ // Returns the human readable name of the supervised user.
+ std::string GetSupervisedUserName() const;
+
// Owns us via the KeyedService mechanism.
Profile* profile_;
ScopedJavaLocalRef<jstring> description = ConvertUTF8ToJavaString(
env, identity_info.identity_status_description);
- Java_WebsiteSettingsPopup_addSection(env, popup_jobject_.obj(), icon_id,
- ConvertUTF8ToJavaString(env, headline).obj(), description.obj());
-
base::string16 certificate_label =
- l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON);
- if (!certificate_label.empty()) {
- Java_WebsiteSettingsPopup_setCertificateViewer(env, popup_jobject_.obj(),
- ConvertUTF16ToJavaString(env, certificate_label).obj());
- }
-
- Java_WebsiteSettingsPopup_addDivider(env, popup_jobject_.obj());
+ l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON);
+ Java_WebsiteSettingsPopup_addCertificateSection(
+ env,
+ popup_jobject_.obj(),
+ icon_id,
+ ConvertUTF8ToJavaString(env, headline).obj(),
+ description.obj(),
+ ConvertUTF16ToJavaString(env, certificate_label).obj());
}
{
ScopedJavaLocalRef<jstring> description = ConvertUTF8ToJavaString(
env, identity_info.connection_status_description);
- Java_WebsiteSettingsPopup_addSection(env, popup_jobject_.obj(), icon_id,
- NULL, description.obj());
-
- Java_WebsiteSettingsPopup_addDivider(env, popup_jobject_.obj());
+ Java_WebsiteSettingsPopup_addDescriptionSection(
+ env, popup_jobject_.obj(), icon_id, NULL, description.obj());
}
Java_WebsiteSettingsPopup_addMoreInfoLink(env, popup_jobject_.obj(),
browser_view->GetBoundsInScreen());
}
manage_passwords_bubble_->GetWidget()->Show();
- manage_passwords_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
}
// static
anchor_view_(anchor_view),
never_save_passwords_(false) {
// Compensate for built-in vertical padding in the anchor view's image.
- set_anchor_view_insets(gfx::Insets(2, 0, 2, 0));
+ set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
set_notify_enter_exit_on_child(true);
if (anchor_view)
anchor_view->SetActive(true);
kViewSpacing - title_left;
}
gfx::Rect rect(title_left, lb.y(), std::max(title_width, 0), lb.height());
+ const int title_height = title_->GetPreferredSize().height();
+ if (title_height > rect.height()) {
+ rect.set_y(lb.y() - (title_height - rect.height()) / 2);
+ rect.set_height(title_height);
+ }
rect.set_x(GetMirroredXForRect(rect));
title_->SetBoundsRect(rect);
}
return NULL;
if (window->id() == ash::kShellWindowId_PhantomWindow ||
- window->id() == ash::kShellWindowId_OverlayContainer)
+ window->id() == ash::kShellWindowId_OverlayContainer ||
+ window->id() == ash::kShellWindowId_MouseCursorContainer)
return NULL;
if (window->layer()->type() == ui::LAYER_TEXTURED)
#include "chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h"
#include "base/bind.h"
+#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/metrics/histogram.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
#include "chrome/common/pref_names.h"
+#include "chromeos/chromeos_switches.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
show_on_init_ = true;
return;
}
- core_oobe_actor_->InitDemoModeDetection();
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableDemoMode))
+ core_oobe_actor_->InitDemoModeDetection();
input_service_proxy_.AddObserver(this);
first_time_screen_show_ = true;
GetDevicesFirstTime();
} // namespace
-KioskAppMenuHandler::KioskAppMenuHandler()
+KioskAppMenuHandler::KioskAppMenuHandler(
+ const scoped_refptr<NetworkStateInformer>& network_state_informer)
: weak_ptr_factory_(this),
- is_webui_initialized_(false) {
+ is_webui_initialized_(false),
+ network_state_informer_(network_state_informer) {
KioskAppManager::Get()->AddObserver(this);
+ network_state_informer_->AddObserver(this);
}
KioskAppMenuHandler::~KioskAppMenuHandler() {
KioskAppManager::Get()->RemoveObserver(this);
+ network_state_informer_->RemoveObserver(this);
}
void KioskAppMenuHandler::GetLocalizedStrings(
const base::ListValue* args) {
is_webui_initialized_ = true;
SendKioskApps();
+ UpdateState(ErrorScreenActor::ERROR_REASON_UPDATE);
}
void KioskAppMenuHandler::HandleKioskAppsLoaded(
SendKioskApps();
}
+void KioskAppMenuHandler::UpdateState(ErrorScreenActor::ErrorReason reason) {
+ if (network_state_informer_->state() == NetworkStateInformer::ONLINE)
+ KioskAppManager::Get()->RetryFailedAppDataFetch();
+}
+
} // namespace chromeos
#include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
+#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
#include "content/public/browser/web_ui_message_handler.h"
namespace chromeos {
// KioskAppMenuHandler supplies kiosk apps data to apps menu on sign-in
// screen when app mode is enabled and handles "launchKioskApp" request
// from the apps menu.
-class KioskAppMenuHandler : public content::WebUIMessageHandler,
- public KioskAppManagerObserver {
+class KioskAppMenuHandler
+ : public content::WebUIMessageHandler,
+ public KioskAppManagerObserver,
+ public NetworkStateInformer::NetworkStateInformerObserver {
public:
- KioskAppMenuHandler();
+ explicit KioskAppMenuHandler(
+ const scoped_refptr<NetworkStateInformer>& network_state_informer);
virtual ~KioskAppMenuHandler();
void GetLocalizedStrings(base::DictionaryValue* localized_strings);
virtual void OnKioskAppsSettingsChanged() OVERRIDE;
virtual void OnKioskAppDataChanged(const std::string& app_id) OVERRIDE;
+ // NetworkStateInformer::NetworkStateInformerObserver overrides:
+ virtual void UpdateState(ErrorScreenActor::ErrorReason reason) OVERRIDE;
+
base::WeakPtrFactory<KioskAppMenuHandler> weak_ptr_factory_;
// True when WebUI is initialized. Otherwise don't allow calling JS functions.
bool is_webui_initialized_;
+ scoped_refptr<NetworkStateInformer> network_state_informer_;
+
DISALLOW_COPY_AND_ASSIGN(KioskAppMenuHandler);
};
app_launch_splash_screen_actor_ = app_launch_splash_screen_handler;
// Initialize KioskAppMenuHandler. Note that it is NOT a screen handler.
- kiosk_app_menu_handler_ = new KioskAppMenuHandler;
+ kiosk_app_menu_handler_ = new KioskAppMenuHandler(network_state_informer_);
web_ui->AddMessageHandler(kiosk_app_menu_handler_);
base::DictionaryValue localized_strings;
cvox.ChromeVoxEventWatcher.mutationHandler=function(a){if(cvox.ChromeVoxEventSuspender.areEventsSuspended())return!0;cvox.ChromeVox.navigationManager.updateIndicatorIfChanged();cvox.LiveRegions.processMutations(a,function(a,c){var d=new window.Event("LiveRegion");d.navDescriptions=c;d.assertive=a;cvox.ChromeVoxEventWatcher.addEvent(d,!0);return!0})};
cvox.ChromeVoxEventWatcher.mouseClickEventWatcher=function(a){if(a.fromCvox)return!0;if(cvox.ChromeVox.host.mustRedispatchClickEvent())return cvox.ChromeVoxUserCommands.wasMouseClicked=!0,a.stopPropagation(),a.preventDefault(),cvox.Focuser.setFocus(cvox.ChromeVox.navigationManager.getCurrentNode()),cvox.ChromeVox.tts.speak(cvox.ChromeVox.msgs.getMsg("element_clicked"),cvox.ChromeVoxEventWatcher.queueMode_(),cvox.AbstractTts.PERSONALITY_ANNOTATION),a=cvox.ChromeVox.navigationManager.getCurrentNode(),
cvox.DomUtil.clickElem(a,!1,!0),!1;cvox.ChromeVoxEventWatcher.addEvent(a);return cvox.ChromeVoxUserCommands.wasMouseClicked=!0};
-cvox.ChromeVoxEventWatcher.mouseOverEventWatcher=function(a){var b="ontouchstart"in window,c=cvox.ChromeVoxEventWatcher.mouseoverDelayMs;if(b)c=0;else if(!cvox.ChromeVoxEventWatcher.focusFollowsMouse)return!0;if(cvox.DomUtil.isDescendantOfNode(cvox.ChromeVoxEventWatcher.announcedMouseOverNode,a.target)||a.target==cvox.ChromeVoxEventWatcher.pendingMouseOverNode)return!0;cvox.ChromeVoxEventWatcher.pendingMouseOverNode=a.target;cvox.ChromeVoxEventWatcher.mouseOverTimeoutId&&(window.clearTimeout(cvox.ChromeVoxEventWatcher.mouseOverTimeoutId),
+cvox.ChromeVoxEventWatcher.mouseOverEventWatcher=function(a){var b=a.metaKey,c=cvox.ChromeVoxEventWatcher.mouseoverDelayMs;if(b)c=0;else if(!cvox.ChromeVoxEventWatcher.focusFollowsMouse)return!0;if(cvox.DomUtil.isDescendantOfNode(cvox.ChromeVoxEventWatcher.announcedMouseOverNode,a.target)||a.target==cvox.ChromeVoxEventWatcher.pendingMouseOverNode)return!0;cvox.ChromeVoxEventWatcher.pendingMouseOverNode=a.target;cvox.ChromeVoxEventWatcher.mouseOverTimeoutId&&(window.clearTimeout(cvox.ChromeVoxEventWatcher.mouseOverTimeoutId),
cvox.ChromeVoxEventWatcher.mouseOverTimeoutId=null);if(a.target.tagName&&"BODY"==a.target.tagName)return cvox.ChromeVoxEventWatcher.pendingMouseOverNode=null,cvox.ChromeVoxEventWatcher.announcedMouseOverNode=null,!0;cvox.ChromeVoxEventWatcher.mouseOverTimeoutId=window.setTimeout(function(){cvox.ChromeVoxEventWatcher.mouseOverTimeoutId=null;if(a.target==cvox.ChromeVoxEventWatcher.pendingMouseOverNode){cvox.ChromeVoxEventWatcher.shouldFlushNextUtterance=!0;cvox.ChromeVox.navigationManager.stopReading(!0);
var b=a.target;cvox.Focuser.setFocus(b);cvox.ApiImplementation.syncToNode(b,!0,cvox.ChromeVoxEventWatcher.queueMode_());cvox.ChromeVoxEventWatcher.announcedMouseOverNode=b}},c);return!0};
cvox.ChromeVoxEventWatcher.mouseOutEventWatcher=function(a){a.target==cvox.ChromeVoxEventWatcher.pendingMouseOverNode&&(cvox.ChromeVoxEventWatcher.pendingMouseOverNode=null,cvox.ChromeVoxEventWatcher.mouseOverTimeoutId&&(window.clearTimeout(cvox.ChromeVoxEventWatcher.mouseOverTimeoutId),cvox.ChromeVoxEventWatcher.mouseOverTimeoutId=null));return!0};
cvox.ChromeVoxEventWatcher.mutationHandler=function(a){if(cvox.ChromeVoxEventSuspender.areEventsSuspended())return!0;cvox.ChromeVox.navigationManager.updateIndicatorIfChanged();cvox.LiveRegions.processMutations(a,function(a,c){var d=new window.Event("LiveRegion");d.navDescriptions=c;d.assertive=a;cvox.ChromeVoxEventWatcher.addEvent(d,!0);return!0})};
cvox.ChromeVoxEventWatcher.mouseClickEventWatcher=function(a){if(a.fromCvox)return!0;if(cvox.ChromeVox.host.mustRedispatchClickEvent())return cvox.ChromeVoxUserCommands.wasMouseClicked=!0,a.stopPropagation(),a.preventDefault(),cvox.Focuser.setFocus(cvox.ChromeVox.navigationManager.getCurrentNode()),cvox.ChromeVox.tts.speak(cvox.ChromeVox.msgs.getMsg("element_clicked"),cvox.ChromeVoxEventWatcher.queueMode_(),cvox.AbstractTts.PERSONALITY_ANNOTATION),a=cvox.ChromeVox.navigationManager.getCurrentNode(),
cvox.DomUtil.clickElem(a,!1,!0),!1;cvox.ChromeVoxEventWatcher.addEvent(a);return cvox.ChromeVoxUserCommands.wasMouseClicked=!0};
-cvox.ChromeVoxEventWatcher.mouseOverEventWatcher=function(a){var b="ontouchstart"in window,c=cvox.ChromeVoxEventWatcher.mouseoverDelayMs;if(b)c=0;else if(!cvox.ChromeVoxEventWatcher.focusFollowsMouse)return!0;if(cvox.DomUtil.isDescendantOfNode(cvox.ChromeVoxEventWatcher.announcedMouseOverNode,a.target)||a.target==cvox.ChromeVoxEventWatcher.pendingMouseOverNode)return!0;cvox.ChromeVoxEventWatcher.pendingMouseOverNode=a.target;cvox.ChromeVoxEventWatcher.mouseOverTimeoutId&&(window.clearTimeout(cvox.ChromeVoxEventWatcher.mouseOverTimeoutId),
+cvox.ChromeVoxEventWatcher.mouseOverEventWatcher=function(a){var b=a.metaKey,c=cvox.ChromeVoxEventWatcher.mouseoverDelayMs;if(b)c=0;else if(!cvox.ChromeVoxEventWatcher.focusFollowsMouse)return!0;if(cvox.DomUtil.isDescendantOfNode(cvox.ChromeVoxEventWatcher.announcedMouseOverNode,a.target)||a.target==cvox.ChromeVoxEventWatcher.pendingMouseOverNode)return!0;cvox.ChromeVoxEventWatcher.pendingMouseOverNode=a.target;cvox.ChromeVoxEventWatcher.mouseOverTimeoutId&&(window.clearTimeout(cvox.ChromeVoxEventWatcher.mouseOverTimeoutId),
cvox.ChromeVoxEventWatcher.mouseOverTimeoutId=null);if(a.target.tagName&&"BODY"==a.target.tagName)return cvox.ChromeVoxEventWatcher.pendingMouseOverNode=null,cvox.ChromeVoxEventWatcher.announcedMouseOverNode=null,!0;cvox.ChromeVoxEventWatcher.mouseOverTimeoutId=window.setTimeout(function(){cvox.ChromeVoxEventWatcher.mouseOverTimeoutId=null;if(a.target==cvox.ChromeVoxEventWatcher.pendingMouseOverNode){cvox.ChromeVoxEventWatcher.shouldFlushNextUtterance=!0;cvox.ChromeVox.navigationManager.stopReading(!0);
var b=a.target;cvox.Focuser.setFocus(b);cvox.ApiImplementation.syncToNode(b,!0,cvox.ChromeVoxEventWatcher.queueMode_());cvox.ChromeVoxEventWatcher.announcedMouseOverNode=b}},c);return!0};
cvox.ChromeVoxEventWatcher.mouseOutEventWatcher=function(a){a.target==cvox.ChromeVoxEventWatcher.pendingMouseOverNode&&(cvox.ChromeVoxEventWatcher.pendingMouseOverNode=null,cvox.ChromeVoxEventWatcher.mouseOverTimeoutId&&(window.clearTimeout(cvox.ChromeVoxEventWatcher.mouseOverTimeoutId),cvox.ChromeVoxEventWatcher.mouseOverTimeoutId=null));return!0};
cvox.ChromeVoxEventWatcher.mutationHandler=function(a){if(cvox.ChromeVoxEventSuspender.areEventsSuspended())return!0;cvox.ChromeVox.navigationManager.updateIndicatorIfChanged();cvox.LiveRegions.processMutations(a,function(a,c){var d=new window.Event("LiveRegion");d.navDescriptions=c;d.assertive=a;cvox.ChromeVoxEventWatcher.addEvent(d,!0);return!0})};
cvox.ChromeVoxEventWatcher.mouseClickEventWatcher=function(a){if(a.fromCvox)return!0;if(cvox.ChromeVox.host.mustRedispatchClickEvent())return cvox.ChromeVoxUserCommands.wasMouseClicked=!0,a.stopPropagation(),a.preventDefault(),cvox.Focuser.setFocus(cvox.ChromeVox.navigationManager.getCurrentNode()),cvox.ChromeVox.tts.speak(cvox.ChromeVox.msgs.getMsg("element_clicked"),cvox.ChromeVoxEventWatcher.queueMode_(),cvox.AbstractTts.PERSONALITY_ANNOTATION),a=cvox.ChromeVox.navigationManager.getCurrentNode(),
cvox.DomUtil.clickElem(a,!1,!0),!1;cvox.ChromeVoxEventWatcher.addEvent(a);return cvox.ChromeVoxUserCommands.wasMouseClicked=!0};
-cvox.ChromeVoxEventWatcher.mouseOverEventWatcher=function(a){var b="ontouchstart"in window,c=cvox.ChromeVoxEventWatcher.mouseoverDelayMs;if(b)c=0;else if(!cvox.ChromeVoxEventWatcher.focusFollowsMouse)return!0;if(cvox.DomUtil.isDescendantOfNode(cvox.ChromeVoxEventWatcher.announcedMouseOverNode,a.target)||a.target==cvox.ChromeVoxEventWatcher.pendingMouseOverNode)return!0;cvox.ChromeVoxEventWatcher.pendingMouseOverNode=a.target;cvox.ChromeVoxEventWatcher.mouseOverTimeoutId&&(window.clearTimeout(cvox.ChromeVoxEventWatcher.mouseOverTimeoutId),
+cvox.ChromeVoxEventWatcher.mouseOverEventWatcher=function(a){var b=a.metaKey,c=cvox.ChromeVoxEventWatcher.mouseoverDelayMs;if(b)c=0;else if(!cvox.ChromeVoxEventWatcher.focusFollowsMouse)return!0;if(cvox.DomUtil.isDescendantOfNode(cvox.ChromeVoxEventWatcher.announcedMouseOverNode,a.target)||a.target==cvox.ChromeVoxEventWatcher.pendingMouseOverNode)return!0;cvox.ChromeVoxEventWatcher.pendingMouseOverNode=a.target;cvox.ChromeVoxEventWatcher.mouseOverTimeoutId&&(window.clearTimeout(cvox.ChromeVoxEventWatcher.mouseOverTimeoutId),
cvox.ChromeVoxEventWatcher.mouseOverTimeoutId=null);if(a.target.tagName&&"BODY"==a.target.tagName)return cvox.ChromeVoxEventWatcher.pendingMouseOverNode=null,cvox.ChromeVoxEventWatcher.announcedMouseOverNode=null,!0;cvox.ChromeVoxEventWatcher.mouseOverTimeoutId=window.setTimeout(function(){cvox.ChromeVoxEventWatcher.mouseOverTimeoutId=null;if(a.target==cvox.ChromeVoxEventWatcher.pendingMouseOverNode){cvox.ChromeVoxEventWatcher.shouldFlushNextUtterance=!0;cvox.ChromeVox.navigationManager.stopReading(!0);
var b=a.target;cvox.Focuser.setFocus(b);cvox.ApiImplementation.syncToNode(b,!0,cvox.ChromeVoxEventWatcher.queueMode_());cvox.ChromeVoxEventWatcher.announcedMouseOverNode=b}},c);return!0};
cvox.ChromeVoxEventWatcher.mouseOutEventWatcher=function(a){a.target==cvox.ChromeVoxEventWatcher.pendingMouseOverNode&&(cvox.ChromeVoxEventWatcher.pendingMouseOverNode=null,cvox.ChromeVoxEventWatcher.mouseOverTimeoutId&&(window.clearTimeout(cvox.ChromeVoxEventWatcher.mouseOverTimeoutId),cvox.ChromeVoxEventWatcher.mouseOverTimeoutId=null));return!0};
// 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).
L"hk.dll", // Unknown (keystroke logger).
+ L"libapi2hook.dll", // V-Bates.
+ L"libinject.dll", // V-Bates.
+ L"libinject2.dll", // V-Bates.
+ L"libredir2.dll", // V-Bates.
L"libsvn_tsvn32.dll", // TortoiseSVN.
+ L"libwinhook.dll", // V-Bates.
L"lmrn.dll", // Unknown.
+ L"minisp.dll", // Unknown (suspected malware).
L"scdetour.dll", // Quick Heal Antivirus.
// See crbug.com/382561.
L"systemk.dll", // Unknown (suspected adware).
اگر این خطمشی را تنظیم کرده باشید، کاربران نمیتوانند آن را تغییر دهند یا لغو نمایند.
چنانچه این خطمشی بدون تنظیم رها شده باشد، نشانگر موشواره بزرگ از ابتدا از کار افتاده است اما میتواند در هر زمان توسط کاربر به کار انداخته شود.</translation>
-<translation id="2633084400146331575">فعال کردن بازخورد صوتی</translation>
+<translation id="2633084400146331575">فعال کردن بازخورد گفتاری</translation>
<translation id="687046793986382807">این خطمشی از <ph name="PRODUCT_NAME"/> نسخه ۳۵ کنار گذاشته شده است.
اطلاعات حافظه به هر حال، بدون در نظر گرفتن مقدار گزینه، به صفحه گزارش داده خواهد شد، اما اندازههای گزارش شده
<translation id="6786747875388722282">Ekstensi</translation>
<translation id="7132877481099023201">URL yang akan diberi akses ke perangkat perekam video tanpa peringatan</translation>
<translation id="8947415621777543415">Laporkan lokasi perangkat</translation>
-<translation id="1655229863189977773">Setel ukuran cache disk dalam byte</translation>
+<translation id="1655229863189977773">Setel ukuran cache disk dalam bita</translation>
<translation id="3358275192586364144">Mengaktifkan pengoptimalan WPAD pada <ph name="PRODUCT_NAME"/> dan mencegah pengguna mengubah setelan ini.
Menyetel kebijakan ini menjadi aktif akan menyebabkan Chrome menunggu dalam interval yang lebih pendek bagi server WPAD berbasis DNS.
Jika kebijakan ini tidak disetel, keyboard di layar akan dinonaktifkan pada awalnya namun dapat diaktifkan oleh pengguna kapan saja. Aturan heuristis juga dapat digunakan untuk menentukan waktu untuk menampilkan keyboard.</translation>
<translation id="6774533686631353488">Izinkan host Native Messaging tingkat pengguna (dipasang tanpa izin admin).</translation>
<translation id="868187325500643455">Izinkan semua situs menjalankan plugin secara otomatis</translation>
-<translation id="7421483919690710988">Setel ukuran cache disk media dalam byte</translation>
+<translation id="7421483919690710988">Setel ukuran cache disk media dalam bita</translation>
<translation id="5226033722357981948">Menentukan apakah pencari plugin harus dinonaktifkan</translation>
<translation id="7234280155140786597">Nama hosting perpesanan asli yang dilarang (atau * untuk semua)</translation>
<translation id="4890209226533226410">Menyetel jenis lup yang diaktifkan.
<translation id="337363190475750230">Онемогућен је</translation>
<translation id="2367567093518048410">Ниво</translation>
<translation id="7610193165460212391">Вредност је изван опсега <ph name="VALUE"/>.</translation>
-<translation id="370665806235115550">Учитавање...</translation>
+<translation id="370665806235115550">Учитава се...</translation>
<translation id="2096368010154057602">Департман</translation>
<translation id="4258748452823770588">Неисправан потпис</translation>
<translation id="3024663005179499861">Погрешан тип смерница</translation>
jfloat touch_major_0,
jfloat touch_major_1,
jfloat raw_pos_x,
- jfloat raw_pos_y) {
+ jfloat raw_pos_y,
+ jint android_tool_type_0,
+ jint android_tool_type_1,
+ jint android_button_state) {
RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
// Avoid synthesizing a touch event if it cannot be forwarded.
if (!rwhv)
touch_major_0,
touch_major_1,
raw_pos_x,
- raw_pos_y);
+ raw_pos_y,
+ android_tool_type_0,
+ android_tool_type_1,
+ android_button_state);
return rwhv->OnTouchEvent(event);
}
SetAccessibilityEnabledInternal(enabled);
}
+void ContentViewCoreImpl::ShowSelectionHandlesAutomatically() const {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj(java_ref_.get(env));
+ if (obj.is_null())
+ return;
+ Java_ContentViewCore_showSelectionHandlesAutomatically(env, obj.obj());
+}
+
void ContentViewCoreImpl::SetAccessibilityEnabledInternal(bool enabled) {
accessibility_enabled_ = enabled;
RenderWidgetHostViewAndroid* host_view = GetRenderWidgetHostViewAndroid();
}
void ContentViewCoreImpl::OnSmartClipDataExtracted(
- const base::string16& result) {
+ const base::string16& text,
+ const base::string16& html,
+ const gfx::Rect& clip_rect) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
return;
- ScopedJavaLocalRef<jstring> jresult = ConvertUTF16ToJavaString(env, result);
+ ScopedJavaLocalRef<jstring> jtext = ConvertUTF16ToJavaString(env, text);
+ ScopedJavaLocalRef<jstring> jhtml = ConvertUTF16ToJavaString(env, html);
+ ScopedJavaLocalRef<jobject> clip_rect_object(CreateJavaRect(env, clip_rect));
Java_ContentViewCore_onSmartClipDataExtracted(
- env, obj.obj(), jresult.obj());
+ env, obj.obj(), jtext.obj(), jhtml.obj(), clip_rect_object.obj());
}
void ContentViewCoreImpl::WebContentsDestroyed() {
jfloat touch_major_0,
jfloat touch_major_1,
jfloat raw_pos_x,
- jfloat raw_pos_y);
+ jfloat raw_pos_y,
+ jint android_tool_type_0,
+ jint android_tool_type_1,
+ jint android_button_state);
jboolean SendMouseMoveEvent(JNIEnv* env,
jobject obj,
jlong time_ms,
// Public methods that call to Java via JNI
// --------------------------------------------------------------------------
- void OnSmartClipDataExtracted(const base::string16& result);
+ void OnSmartClipDataExtracted(const base::string16& text,
+ const base::string16& html,
+ const gfx::Rect& clip_rect);
// Creates a popup menu with |items|.
// |multiple| defines if it should support multi-select.
void SetAccessibilityEnabledInternal(bool enabled);
+ void ShowSelectionHandlesAutomatically() const;
+
// --------------------------------------------------------------------------
// Methods called from native code
// --------------------------------------------------------------------------
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() {
context_provider;
// This check only guarantees the main thread context is created after
// a compositor did successfully initialize hardware draw in the past.
- // In particular this does not guarantee that the main thread context
- // will fail creation when all compositors release hardware draw.
+ // When all compositors have released hardware draw, main thread context
+ // creation is guaranteed to fail.
if (CanCreateMainThreadContext() && !video_context_provider_) {
DCHECK(service_);
DCHECK(share_context_.get());
#include "content/public/common/content_switches.h"
#include "media/base/android/media_player_bridge.h"
#include "media/base/android/media_source_player.h"
+#include "media/base/android/media_url_interceptor.h"
#include "media/base/media_switches.h"
using media::MediaPlayerAndroid;
const int kMediaPlayerThreshold = 1;
static BrowserMediaPlayerManager::Factory g_factory = NULL;
+static media::MediaUrlInterceptor* media_url_interceptor_ = NULL;
// static
void BrowserMediaPlayerManager::RegisterFactory(Factory factory) {
}
// static
+void BrowserMediaPlayerManager::RegisterMediaUrlInterceptor(
+ media::MediaUrlInterceptor* media_url_interceptor) {
+ media_url_interceptor_ = media_url_interceptor;
+}
+
+// static
BrowserMediaPlayerManager* BrowserMediaPlayerManager::Create(
RenderFrameHost* rfh) {
if (g_factory)
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesReleased,
weak_ptr_factory_.GetWeakPtr()),
- media_player_params.frame_url);
+ media_player_params.frame_url,
+ media_player_params.allow_credentials);
BrowserMediaPlayerManager* browser_media_player_manager =
static_cast<BrowserMediaPlayerManager*>(manager);
ContentViewCoreImpl* content_view_core_impl =
return media_resource_getter_.get();
}
+media::MediaUrlInterceptor*
+BrowserMediaPlayerManager::GetMediaUrlInterceptor() {
+ return media_url_interceptor_;
+}
+
MediaPlayerAndroid* BrowserMediaPlayerManager::GetFullscreenPlayer() {
return GetPlayer(fullscreen_player_id_);
}
video_view_->OpenVideo();
return;
} else if (!ContentVideoView::GetInstance()) {
+ if (!GetPlayer(player_id)) {
+ // If a player doesn't exist, it must be waiting for CORS check.
+ // As a result, just request the tab to enter fullscreen mode without
+ // creating the surface view. This is only needed for M37.
+ Send(new MediaPlayerMsg_DidEnterFullscreen(RoutingID(), player_id));
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableOverlayFullscreenVideoSubtitle)) {
+ return;
+ }
+ if (RenderWidgetHostViewAndroid* view_android =
+ static_cast<RenderWidgetHostViewAndroid*>(
+ web_contents_->GetRenderWidgetHostView())) {
+ view_android->SetOverlayVideoMode(true);
+ }
+ if (WebContentsDelegate* delegate = web_contents_->GetDelegate())
+ delegate->ToggleFullscreenModeForTab(web_contents_, true);
+ }
+
// In Android WebView, two ContentViewCores could both try to enter
// fullscreen video, we just ignore the second one.
video_view_.reset(new ContentVideoView(this));
#include "ipc/ipc_message.h"
#include "media/base/android/media_player_android.h"
#include "media/base/android/media_player_manager.h"
+#include "media/base/android/media_url_interceptor.h"
#include "ui/gfx/rect_f.h"
#include "url/gurl.h"
typedef BrowserMediaPlayerManager* (*Factory)(RenderFrameHost*);
static void RegisterFactory(Factory factory);
+ // Permits embedders to handle custom urls.
+ static void RegisterMediaUrlInterceptor(
+ media::MediaUrlInterceptor* media_url_interceptor);
+
// Returns a new instance using the registered factory if available.
static BrowserMediaPlayerManager* Create(RenderFrameHost* rfh);
virtual void OnVideoSizeChanged(
int player_id, int width, int height) OVERRIDE;
virtual media::MediaResourceGetter* GetMediaResourceGetter() OVERRIDE;
+ virtual media::MediaUrlInterceptor* GetMediaUrlInterceptor() OVERRIDE;
virtual media::MediaPlayerAndroid* GetFullscreenPlayer() OVERRIDE;
virtual media::MediaPlayerAndroid* GetPlayer(int player_id) OVERRIDE;
virtual void RequestFullScreen(int player_id) OVERRIDE;
#include "content/public/common/content_client.h"
#include "content/public/common/url_constants.h"
#include "jni/MediaResourceGetter_jni.h"
+#include "media/base/android/media_url_interceptor.h"
+#include "net/base/auth.h"
#include "net/cookies/cookie_monster.h"
#include "net/cookies/cookie_store.h"
#include "net/url_request/url_request_context.h"
ReturnResultOnUIThread(callback, std::string());
}
-// Get the metadata from a media URL. When finished, a task is posted to the UI
+// Posts a task to the UI thread to run the callback function.
+static void PostMediaMetadataCallbackTask(
+ const media::MediaResourceGetter::ExtractMediaMetadataCB& callback,
+ JNIEnv* env, ScopedJavaLocalRef<jobject>& j_metadata) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(callback, base::TimeDelta::FromMilliseconds(
+ Java_MediaMetadata_getDurationInMilliseconds(
+ env, j_metadata.obj())),
+ Java_MediaMetadata_getWidth(env, j_metadata.obj()),
+ Java_MediaMetadata_getHeight(env, j_metadata.obj()),
+ Java_MediaMetadata_isSuccess(env, j_metadata.obj())));
+}
+
+// Gets the metadata from a media URL. When finished, a task is posted to the UI
// thread to run the callback function.
static void GetMediaMetadata(
const std::string& url, const std::string& cookies,
j_url_string.obj(),
j_cookies.obj(),
j_user_agent.obj());
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(callback, base::TimeDelta::FromMilliseconds(
- Java_MediaMetadata_getDurationInMilliseconds(
- env, j_metadata.obj())),
- Java_MediaMetadata_getWidth(env, j_metadata.obj()),
- Java_MediaMetadata_getHeight(env, j_metadata.obj()),
- Java_MediaMetadata_isSuccess(env, j_metadata.obj())));
+
+ PostMediaMetadataCallbackTask(callback, env, j_metadata);
+}
+
+// Gets the metadata from a file descriptor. When finished, a task is posted to
+// the UI thread to run the callback function.
+static void GetMediaMetadataFromFd(
+ const int fd, const int64 offset, const int64 size,
+ const media::MediaResourceGetter::ExtractMediaMetadataCB& callback) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+
+ ScopedJavaLocalRef<jobject> j_metadata =
+ Java_MediaResourceGetter_extractMediaMetadataFromFd(
+ env, fd, offset, size);
+
+ PostMediaMetadataCallbackTask(callback, env, j_metadata);
}
// The task object that retrieves cookie on the IO thread.
base::Bind(&GetMediaMetadata, url, cookies, user_agent, callback));
}
+void MediaResourceGetterImpl::ExtractMediaMetadata(
+ const int fd, const int64 offset, const int64 size,
+ const ExtractMediaMetadataCB& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
+ pool->PostWorkerTask(
+ FROM_HERE,
+ base::Bind(&GetMediaMetadataFromFd, fd, offset, size, callback));
+}
+
// static
bool MediaResourceGetterImpl::RegisterMediaResourceGetter(JNIEnv* env) {
return RegisterNativesImpl(env);
#include "base/memory/weak_ptr.h"
#include "base/synchronization/waitable_event.h"
#include "media/base/android/media_resource_getter.h"
+#include "media/base/android/media_url_interceptor.h"
+#include "net/base/auth.h"
#include "net/cookies/canonical_cookie.h"
namespace fileapi {
const std::string& url, const std::string& cookies,
const std::string& user_agent,
const ExtractMediaMetadataCB& callback) OVERRIDE;
+ virtual void ExtractMediaMetadata(
+ const int fd,
+ const int64 offset,
+ const int64 size,
+ const ExtractMediaMetadataCB& callback) OVERRIDE;
static bool RegisterMediaResourceGetter(JNIEnv* env);
--- /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 "content/browser/renderer_host/input/gesture_text_selector.h"
+
+#include "ui/events/event_constants.h"
+#include "ui/events/gesture_detection/gesture_event_data.h"
+#include "ui/events/gesture_detection/motion_event.h"
+
+namespace content {
+
+GestureTextSelector::GestureTextSelector(GestureTextSelectorClient* client)
+ : client_(client),
+ text_selection_triggered_(false),
+ anchor_x_(0.0f),
+ anchor_y_(0.0f) {
+}
+
+GestureTextSelector::~GestureTextSelector() {
+}
+
+bool GestureTextSelector::OnTouchEvent(const ui::MotionEvent& event) {
+ if (event.GetAction() == ui::MotionEvent::ACTION_DOWN) {
+ // Only trigger selection on ACTION_DOWN to prevent partial touch or gesture
+ // sequences from being forwarded.
+ text_selection_triggered_ = ShouldStartTextSelection(event);
+ }
+ return text_selection_triggered_;
+}
+
+bool GestureTextSelector::OnGestureEvent(const ui::GestureEventData& gesture) {
+ if (!text_selection_triggered_)
+ return false;
+
+ switch (gesture.type()) {
+ case ui::ET_GESTURE_TAP: {
+ client_->LongPress(gesture.time, gesture.x, gesture.y);
+ break;
+ }
+ case ui::ET_GESTURE_SCROLL_BEGIN: {
+ client_->Unselect();
+ anchor_x_ = gesture.x;
+ anchor_y_ = gesture.y;
+ break;
+ }
+ case ui::ET_GESTURE_SCROLL_UPDATE: {
+ // TODO(changwan): check if we can show handles on ET_GESTURE_SCROLL_END
+ // instead. Currently it is not possible as ShowSelectionHandles should
+ // be called before we change the selection.
+ client_->ShowSelectionHandlesAutomatically();
+ client_->SelectRange(anchor_x_, anchor_y_, gesture.x, gesture.y);
+ break;
+ }
+ default:
+ // Suppress all other gestures when we are selecting text.
+ break;
+ }
+ return true;
+}
+
+// static
+bool GestureTextSelector::ShouldStartTextSelection(
+ const ui::MotionEvent& event) {
+ DCHECK_GT(event.GetPointerCount(), 0u);
+ // Currently we are supporting stylus-only cases.
+ const bool is_stylus =
+ event.GetToolType(0) == ui::MotionEvent::TOOL_TYPE_STYLUS;
+ const bool is_only_secondary_button_pressed =
+ event.GetButtonState() == ui::MotionEvent::BUTTON_SECONDARY;
+ return is_stylus && is_only_secondary_button_pressed;
+}
+
+} // namespace content
--- /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 CONTENT_BROWSER_RENDERER_HOST_INPUT_GESTURE_TEXT_SELECTOR_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_GESTURE_TEXT_SELECTOR_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "content/common/content_export.h"
+
+namespace ui {
+struct GestureEventData;
+class MotionEvent;
+}
+
+namespace content {
+class GestureTextSelectorTest;
+
+// Interface with which GestureTextSelector can select, unselect, show
+// selection handles, or long press.
+class CONTENT_EXPORT GestureTextSelectorClient {
+ public:
+ virtual ~GestureTextSelectorClient() {}
+
+ virtual void ShowSelectionHandlesAutomatically() = 0;
+ virtual void SelectRange(float x1, float y1, float x2, float y2) = 0;
+ virtual void Unselect() = 0;
+ virtual void LongPress(base::TimeTicks time, float x, float y) = 0;
+};
+
+// A class to handle gesture-based text selection, such as when clicking first
+// button on stylus input. It also generates a synthetic long press gesture on
+// tap so that a word can be selected or the contextual menu can be shown.
+class CONTENT_EXPORT GestureTextSelector {
+ public:
+ explicit GestureTextSelector(GestureTextSelectorClient* client);
+ virtual ~GestureTextSelector();
+
+ // This should be called after gesture detection but before associated
+ // gestures are dispatched. Returns whether it will consume |event|.
+ bool OnTouchEvent(const ui::MotionEvent& event);
+
+ // Returns whether it will consume the event.
+ bool OnGestureEvent(const ui::GestureEventData& gesture);
+
+ private:
+ friend class GestureTextSelectorTest;
+ FRIEND_TEST_ALL_PREFIXES(GestureTextSelectorTest,
+ ShouldStartTextSelection);
+
+ static bool ShouldStartTextSelection(const ui::MotionEvent& event);
+
+ GestureTextSelectorClient* client_;
+ bool text_selection_triggered_;
+ float anchor_x_;
+ float anchor_y_;
+
+ DISALLOW_COPY_AND_ASSIGN(GestureTextSelector);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_GESTURE_TEXT_SELECTOR_H_
--- /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 <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "content/browser/renderer_host/input/gesture_text_selector.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/gesture_detection/gesture_event_data.h"
+#include "ui/events/gesture_detection/motion_event.h"
+#include "ui/events/test/mock_motion_event.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+using ui::GestureEventData;
+using ui::GestureEventDetails;
+using ui::MotionEvent;
+using ui::test::MockMotionEvent;
+
+namespace content {
+
+class GestureTextSelectorTest : public testing::Test,
+ public GestureTextSelectorClient {
+ public:
+ GestureTextSelectorTest() {}
+ virtual ~GestureTextSelectorTest() {}
+
+ // Test implementation.
+ virtual void SetUp() OVERRIDE {
+ selector_.reset(new GestureTextSelector(this));
+ event_log_.clear();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ selector_.reset();
+ event_log_.clear();
+ }
+
+ // GestureTextSelectorClient implementation.
+ virtual void ShowSelectionHandlesAutomatically() OVERRIDE {
+ event_log_.push_back("Show");
+ }
+
+ virtual void SelectRange(float x1, float y1, float x2, float y2) OVERRIDE {
+ event_log_.push_back("SelectRange");
+ }
+
+ virtual void Unselect() OVERRIDE {
+ event_log_.push_back("Unselect");
+ }
+
+ virtual void LongPress(base::TimeTicks time, float x, float y) OVERRIDE {
+ event_log_.push_back("LongPress");
+ }
+
+ protected:
+ scoped_ptr<GestureTextSelector> selector_;
+ std::vector<std::string> event_log_;
+};
+
+TEST_F(GestureTextSelectorTest, ShouldStartTextSelection) {
+ base::TimeTicks event_time = base::TimeTicks::Now();
+ { // Touched with a finger.
+ MockMotionEvent e(MotionEvent::ACTION_DOWN, event_time, 50.0f, 50.0f);
+ e.SetToolType(0, MotionEvent::TOOL_TYPE_FINGER);
+ e.SetButtonState(0);
+ EXPECT_FALSE(selector_->ShouldStartTextSelection(e));
+ }
+
+ { // Touched with a stylus, but no button pressed.
+ MockMotionEvent e(MotionEvent::ACTION_DOWN, event_time, 50.0f, 50.0f);
+ e.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+ e.SetButtonState(0);
+ EXPECT_FALSE(selector_->ShouldStartTextSelection(e));
+ }
+
+ { // Touched with a stylus, with first button (BUTTON_SECONDARY) pressed.
+ MockMotionEvent e(MotionEvent::ACTION_DOWN, event_time, 50.0f, 50.0f);
+ e.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+ e.SetButtonState(MotionEvent::BUTTON_SECONDARY);
+ EXPECT_TRUE(selector_->ShouldStartTextSelection(e));
+ }
+
+ { // Touched with a stylus, with two buttons pressed.
+ MockMotionEvent e(MotionEvent::ACTION_DOWN, event_time, 50.0f, 50.0f);
+ e.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+ e.SetButtonState(
+ MotionEvent::BUTTON_SECONDARY | MotionEvent::BUTTON_TERTIARY);
+ EXPECT_FALSE(selector_->ShouldStartTextSelection(e));
+ }
+}
+
+TEST_F(GestureTextSelectorTest, FingerTouch) {
+ base::TimeTicks event_time = base::TimeTicks::Now();
+ const float x = 50.0f;
+ const float y = 30.0f;
+ // 1. Touched with a finger: ignored
+ MockMotionEvent finger(MotionEvent::ACTION_DOWN, event_time, x, y);
+ finger.SetToolType(0, MotionEvent::TOOL_TYPE_FINGER);
+ EXPECT_FALSE(selector_->OnTouchEvent(finger));
+ // We do not consume finger events.
+ EXPECT_TRUE(event_log_.empty());
+}
+
+TEST_F(GestureTextSelectorTest, PenDragging) {
+ base::TimeTicks event_time = base::TimeTicks::Now();
+ const float x1 = 50.0f;
+ const float y1 = 30.0f;
+ const float x2 = 100.0f;
+ const float y2 = 90.0f;
+ // 1. ACTION_DOWN with stylus + button
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ MockMotionEvent action_down(MotionEvent::ACTION_DOWN, event_time, x1, y1);
+ action_down.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+ action_down.SetButtonState(MotionEvent::BUTTON_SECONDARY);
+ EXPECT_TRUE(selector_->OnTouchEvent(action_down));
+ EXPECT_TRUE(event_log_.empty());
+
+ // 2. ACTION_MOVE
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ MockMotionEvent action_move(MotionEvent::ACTION_MOVE, event_time, x2, y2);
+ action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+ action_move.SetButtonState(MotionEvent::BUTTON_SECONDARY);
+ EXPECT_TRUE(selector_->OnTouchEvent(action_move));
+ EXPECT_TRUE(event_log_.empty());
+
+ // 3. DOUBLE TAP
+ // Suppress most gesture events when in text selection mode.
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ const GestureEventData double_tap(
+ GestureEventDetails(ui::ET_GESTURE_DOUBLE_TAP, 0, 0), 0, event_time,
+ x2, y2, x2, y2, 1, gfx::RectF(0, 0, 0, 0));
+ EXPECT_TRUE(selector_->OnGestureEvent(double_tap));
+ EXPECT_TRUE(event_log_.empty());
+
+ // 4. ET_GESTURE_SCROLL_BEGIN
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ const GestureEventData scroll_begin(
+ GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0), 0, event_time,
+ x1, y1, x1, y1, 1, gfx::RectF(0, 0, 0, 0));
+ EXPECT_TRUE(selector_->OnGestureEvent(scroll_begin));
+ EXPECT_EQ(1u, event_log_.size()); // Unselect
+
+ // 5. ET_GESTURE_SCROLL_UPDATE
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ const GestureEventData scroll_update(
+ GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 0, 0), 0, event_time,
+ x2, y2, x2, y2, 1, gfx::RectF(0, 0, 0, 0));
+ EXPECT_TRUE(selector_->OnGestureEvent(scroll_update));
+ EXPECT_EQ(3u, event_log_.size()); // Unselect, Show, SelectRange
+ EXPECT_STREQ("SelectRange", event_log_.back().c_str());
+
+ // 6. ACTION_UP
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ MockMotionEvent action_up(MotionEvent::ACTION_UP, event_time, x2, y2);
+ action_up.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+ action_up.SetButtonState(0);
+ EXPECT_TRUE(selector_->OnTouchEvent(action_up));
+ EXPECT_EQ(3u, event_log_.size()); // NO CHANGE
+
+ // 7. ET_GESTURE_SCROLL_END
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ const GestureEventData scroll_end(
+ GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0), 0, event_time,
+ x2, y2, x2, y2, 1, gfx::RectF(0, 0, 0, 0));
+ EXPECT_TRUE(selector_->OnGestureEvent(scroll_end));
+ EXPECT_EQ(3u, event_log_.size()); // NO CHANGE
+}
+
+TEST_F(GestureTextSelectorTest, TapToSelectWord) {
+ base::TimeTicks event_time = base::TimeTicks::Now();
+ const float x1 = 50.0f;
+ const float y1 = 30.0f;
+ const float x2 = 51.0f;
+ const float y2 = 31.0f;
+ // 1. ACTION_DOWN with stylus + button
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ MockMotionEvent action_down(MotionEvent::ACTION_DOWN, event_time, x1, y1);
+ action_down.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+ action_down.SetButtonState(MotionEvent::BUTTON_SECONDARY);
+ EXPECT_TRUE(selector_->OnTouchEvent(action_down));
+ EXPECT_TRUE(event_log_.empty());
+
+ // 5. TAP_DOWN
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ const GestureEventData tap_down(
+ GestureEventDetails(ui::ET_GESTURE_TAP_DOWN, 0, 0), 0, event_time,
+ x2, y2, x2, y2, 1, gfx::RectF(0, 0, 0, 0));
+ EXPECT_TRUE(selector_->OnGestureEvent(tap_down));
+ EXPECT_TRUE(event_log_.empty());
+
+ // 2. ACTION_MOVE
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ MockMotionEvent action_move(MotionEvent::ACTION_MOVE, event_time, x2, y2);
+ action_move.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+ action_move.SetButtonState(MotionEvent::BUTTON_SECONDARY);
+ EXPECT_TRUE(selector_->OnTouchEvent(action_move));
+ EXPECT_TRUE(event_log_.empty());
+
+ // 3. ACTION_UP
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ MockMotionEvent action_up(MotionEvent::ACTION_UP, event_time, x2, y2);
+ action_up.SetToolType(0, MotionEvent::TOOL_TYPE_STYLUS);
+ action_up.SetButtonState(0);
+ EXPECT_TRUE(selector_->OnTouchEvent(action_up));
+ EXPECT_TRUE(event_log_.empty());
+
+ // 4. TAP
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ const GestureEventData tap(
+ GestureEventDetails(ui::ET_GESTURE_TAP, 0, 0), 0, event_time,
+ x1, y1, x1, y1, 1, gfx::RectF(0, 0, 0, 0));
+ EXPECT_TRUE(selector_->OnGestureEvent(tap));
+ EXPECT_EQ(1u, event_log_.size()); // LongPress
+ EXPECT_STREQ("LongPress", event_log_.back().c_str());
+}
+
+} // namespace content
return MotionEventAndroid::ACTION_CANCEL;
}
+MotionEventAndroid::ToolType FromAndroidToolType(int android_tool_type) {
+ switch (android_tool_type) {
+ case TOOL_TYPE_UNKNOWN:
+ return MotionEventAndroid::TOOL_TYPE_UNKNOWN;
+ case TOOL_TYPE_FINGER:
+ return MotionEventAndroid::TOOL_TYPE_FINGER;
+ case TOOL_TYPE_STYLUS:
+ return MotionEventAndroid::TOOL_TYPE_STYLUS;
+ case TOOL_TYPE_MOUSE:
+ return MotionEventAndroid::TOOL_TYPE_MOUSE;
+ default:
+ NOTREACHED() << "Invalid Android MotionEvent tool type: "
+ << android_tool_type;
+ };
+ return MotionEventAndroid::TOOL_TYPE_UNKNOWN;
+}
+
+int FromAndroidButtonState(int button_state) {
+ int result = 0;
+ if ((button_state & BUTTON_BACK) != 0)
+ result |= MotionEventAndroid::BUTTON_BACK;
+ if ((button_state & BUTTON_FORWARD) != 0)
+ result |= MotionEventAndroid::BUTTON_FORWARD;
+ if ((button_state & BUTTON_PRIMARY) != 0)
+ result |= MotionEventAndroid::BUTTON_PRIMARY;
+ if ((button_state & BUTTON_SECONDARY) != 0)
+ result |= MotionEventAndroid::BUTTON_SECONDARY;
+ if ((button_state & BUTTON_TERTIARY) != 0)
+ result |= MotionEventAndroid::BUTTON_TERTIARY;
+ return result;
+}
+
int64 ToAndroidTime(base::TimeTicks time) {
return (time - base::TimeTicks()).InMilliseconds();
}
jfloat touch_major_0_pixels,
jfloat touch_major_1_pixels,
jfloat raw_pos_x_pixels,
- jfloat raw_pos_y_pixels)
+ jfloat raw_pos_y_pixels,
+ jint android_tool_type_0,
+ jint android_tool_type_1,
+ jint android_button_state)
: cached_time_(FromAndroidTime(time_ms)),
cached_action_(FromAndroidAction(android_action)),
cached_pointer_count_(pointer_count),
cached_history_size_(history_size),
cached_action_index_(action_index),
+ cached_button_state_(FromAndroidButtonState(android_button_state)),
pix_to_dip_(pix_to_dip),
should_recycle_(false) {
DCHECK_GT(pointer_count, 0);
cached_raw_position_offset_ =
ToDips(gfx::PointF(raw_pos_x_pixels, raw_pos_y_pixels)) -
cached_positions_[0];
+ cached_tool_types_[0] = FromAndroidToolType(android_tool_type_0);
+ cached_tool_types_[1] = FromAndroidToolType(android_tool_type_1);
}
MotionEventAndroid::MotionEventAndroid(float pix_to_dip,
cached_pointer_count_(Java_MotionEvent_getPointerCount(env, event)),
cached_history_size_(Java_MotionEvent_getHistorySize(env, event)),
cached_action_index_(Java_MotionEvent_getActionIndex(env, event)),
+ cached_button_state_(
+ FromAndroidButtonState(Java_MotionEvent_getButtonState(env, event))),
pix_to_dip_(pix_to_dip),
should_recycle_(true) {
event_.Reset(env, event);
cached_pointer_ids_[i] = Java_MotionEvent_getPointerId(env, event, i);
cached_touch_majors_[i] =
ToDips(Java_MotionEvent_getTouchMajorF_I(env, event, i));
+ cached_tool_types_[i] =
+ FromAndroidToolType(Java_MotionEvent_getToolType(env, event, i));
} else {
cached_pointer_ids_[i] = 0;
cached_touch_majors_[i] = 0.f;
+ cached_tool_types_[i] = MotionEvent::TOOL_TYPE_UNKNOWN;
}
}
cached_history_size_(other.cached_history_size_),
cached_action_index_(other.cached_action_index_),
cached_raw_position_offset_(other.cached_raw_position_offset_),
+ cached_button_state_(other.cached_button_state_),
pix_to_dip_(other.pix_to_dip_),
should_recycle_(true) {
DCHECK(event_.obj());
cached_positions_[i] = other.cached_positions_[i];
cached_pointer_ids_[i] = other.cached_pointer_ids_[i];
cached_touch_majors_[i] = other.cached_touch_majors_[i];
+ cached_tool_types_[i] = other.cached_tool_types_[i];
}
}
AttachCurrentThread(), event_.obj(), pointer_index, historical_index));
}
+ui::MotionEvent::ToolType MotionEventAndroid::GetToolType(
+ size_t pointer_index) const {
+ DCHECK_LT(pointer_index, cached_pointer_count_);
+ if (pointer_index < MAX_POINTERS_TO_CACHE)
+ return cached_tool_types_[pointer_index];
+ return FromAndroidToolType(Java_MotionEvent_getToolType(
+ AttachCurrentThread(), event_.obj(), pointer_index));
+}
+
+int MotionEventAndroid::GetButtonState() const {
+ return cached_button_state_;
+}
+
scoped_ptr<ui::MotionEvent> MotionEventAndroid::Clone() const {
return scoped_ptr<MotionEvent>(new MotionEventAndroid(*this));
}
jfloat touch_major_0_pixels,
jfloat touch_major_1_pixels,
jfloat raw_pos_x_pixels,
- jfloat raw_pos_y_pixels);
+ jfloat raw_pos_y_pixels,
+ jint android_tool_type_0,
+ jint android_tool_type_1,
+ jint android_button_state);
virtual ~MotionEventAndroid();
// ui::MotionEvent methods.
size_t historical_index) const OVERRIDE;
virtual float GetHistoricalY(size_t pointer_index,
size_t historical_index) const OVERRIDE;
+ virtual ToolType GetToolType(size_t pointer_index) const OVERRIDE;
+ virtual int GetButtonState() const OVERRIDE;
virtual scoped_ptr<MotionEvent> Clone() const OVERRIDE;
virtual scoped_ptr<MotionEvent> Cancel() const OVERRIDE;
int cached_pointer_ids_[MAX_POINTERS_TO_CACHE];
float cached_touch_majors_[MAX_POINTERS_TO_CACHE];
gfx::Vector2dF cached_raw_position_offset_;
+ ToolType cached_tool_types_[MAX_POINTERS_TO_CACHE];
+ int cached_button_state_;
// Used to convert pixel coordinates from the Java-backed MotionEvent to
// DIP coordinates cached/returned by the MotionEventAndroid.
return 0.f;
}
+ui::MotionEvent::ToolType MotionEventWeb::GetToolType(
+ size_t pointer_index) const {
+ NOTIMPLEMENTED();
+ return TOOL_TYPE_UNKNOWN;
+}
+
+int MotionEventWeb::GetButtonState() const {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
scoped_ptr<ui::MotionEvent> MotionEventWeb::Clone() const {
return scoped_ptr<MotionEvent>(new MotionEventWeb(event_));
}
virtual float GetHistoricalY(
size_t pointer_index,
size_t historical_index) const OVERRIDE;
+ virtual ToolType GetToolType(size_t pointer_index) const OVERRIDE;
+ virtual int GetButtonState() const OVERRIDE;
virtual scoped_ptr<MotionEvent> Clone() const OVERRIDE;
virtual scoped_ptr<MotionEvent> Cancel() const OVERRIDE;
// Reset some fields in preparation for recovering from a crash.
ResetSizeAndRepaintPendingFlags();
current_size_.SetSize(0, 0);
+ // After the renderer crashes, the view is destroyed and so the
+ // RenderWidgetHost cannot track its visibility anymore. We assume such
+ // RenderWidgetHost to be visible for the sake of internal accounting - be
+ // careful about changing this - see http://crbug.com/401859.
+ //
+ // 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_) {
+ process_->WidgetRestored();
+ is_hidden_ = false;
+ }
// Reset this to ensure the hung renderer mechanism is working properly.
in_flight_event_count_ = 0;
// Indicates whether a page is loading or not.
bool is_loading_;
- // Indicates whether a page is hidden or not.
+ // Indicates whether a page is hidden or not. It has to stay in sync with the
+ // most recent call to process_->WidgetRestored() / WidgetHidden().
bool is_hidden_;
// Indicates whether a page is fullscreen or not.
#include "content/browser/renderer_host/dip_util.h"
#include "content/browser/renderer_host/image_transport_factory_android.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target_android.h"
+#include "content/browser/renderer_host/input/web_input_event_builders_android.h"
#include "content/browser/renderer_host/input/web_input_event_util.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
switches::kDisableOverscrollEdgeEffect)),
overscroll_effect_(OverscrollGlow::Create(overscroll_effect_enabled_)),
gesture_provider_(CreateGestureProviderConfig(), this),
+ gesture_text_selector_(this),
flush_input_requested_(false),
accelerated_surface_route_id_(0),
using_synchronous_compositor_(SynchronousCompositorImpl::FromID(
}
void RenderWidgetHostViewAndroid::OnSmartClipDataExtracted(
- const base::string16& result) {
- // Custom serialization over IPC isn't allowed normally for security reasons.
- // Since this feature is only used in (single-process) WebView, there are no
- // security issues. Enforce that it's only called in single process mode.
- CHECK(RenderProcessHost::run_renderer_in_process());
+ const base::string16& text,
+ const base::string16& html,
+ const gfx::Rect rect) {
if (content_view_core_)
- content_view_core_->OnSmartClipDataExtracted(result);
+ content_view_core_->OnSmartClipDataExtracted(text, html, rect);
}
bool RenderWidgetHostViewAndroid::OnTouchEvent(
if (!gesture_provider_.OnTouchEvent(event))
return false;
+ if (gesture_text_selector_.OnTouchEvent(event)) {
+ gesture_provider_.OnTouchEventAck(false);
+ return true;
+ }
+
// Short-circuit touch forwarding if no touch handlers exist.
if (!host_->ShouldForwardTouchEvent()) {
const bool event_consumed = false;
void RenderWidgetHostViewAndroid::OnGestureEvent(
const ui::GestureEventData& gesture) {
+ if (gesture_text_selector_.OnGestureEvent(gesture))
+ return;
+
SendGestureEvent(CreateWebGestureEventFromGestureEventData(gesture));
}
return SkBitmap::kARGB_8888_Config;
}
+void RenderWidgetHostViewAndroid::ShowSelectionHandlesAutomatically() {
+ if (content_view_core_)
+ content_view_core_->ShowSelectionHandlesAutomatically();
+}
+
+void RenderWidgetHostViewAndroid::SelectRange(
+ float x1, float y1, float x2, float y2) {
+ if (content_view_core_)
+ static_cast<WebContentsImpl*>(content_view_core_->GetWebContents())->
+ SelectRange(gfx::Point(x1, y1), gfx::Point(x2, y2));
+}
+
+void RenderWidgetHostViewAndroid::Unselect() {
+ if (content_view_core_)
+ content_view_core_->GetWebContents()->Unselect();
+}
+
+void RenderWidgetHostViewAndroid::LongPress(
+ base::TimeTicks time, float x, float y) {
+ blink::WebGestureEvent long_press = WebGestureEventBuilder::Build(
+ blink::WebInputEvent::GestureLongPress,
+ (time - base::TimeTicks()).InSecondsF(), x, y);
+ SendGestureEvent(long_press);
+}
+
// static
void RenderWidgetHostViewBase::GetDefaultScreenInfo(
blink::WebScreenInfo* results) {
#include "content/browser/renderer_host/delegated_frame_evictor.h"
#include "content/browser/renderer_host/image_transport_factory_android.h"
#include "content/browser/renderer_host/ime_adapter_android.h"
+#include "content/browser/renderer_host/input/gesture_text_selector.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/common/content_export.h"
#include "gpu/command_buffer/common/mailbox.h"
public ImageTransportFactoryAndroidObserver,
public ui::GestureProviderClient,
public ui::WindowAndroidObserver,
- public DelegatedFrameEvictorClient {
+ public DelegatedFrameEvictorClient,
+ public GestureTextSelectorClient {
public:
RenderWidgetHostViewAndroid(RenderWidgetHostImpl* widget,
ContentViewCoreImpl* content_view_core);
virtual SkBitmap::Config PreferredReadbackFormat() OVERRIDE;
+ // GestureTextSelectorClient implementation.
+ virtual void ShowSelectionHandlesAutomatically() OVERRIDE;
+ virtual void SelectRange(float x1, float y1, float x2, float y2) OVERRIDE;
+ virtual void Unselect() OVERRIDE;
+ virtual void LongPress(base::TimeTicks time, float x, float y) OVERRIDE;
+
// Non-virtual methods
void SetContentViewCore(ContentViewCoreImpl* content_view_core);
SkColor GetCachedBackgroundColor() const;
void OnDidChangeBodyBackgroundColor(SkColor color);
void OnStartContentIntent(const GURL& content_url);
void OnSetNeedsBeginFrame(bool enabled);
- void OnSmartClipDataExtracted(const base::string16& result);
+ void OnSmartClipDataExtracted(const base::string16& text,
+ const base::string16& html,
+ const gfx::Rect rect);
bool OnTouchEvent(const ui::MotionEvent& event);
void ResetGestureDetection();
// Android MotionEvent's) and touch event acks.
ui::FilteredGestureProvider gesture_provider_;
+ // Handles gesture based text selection
+ GestureTextSelector gesture_text_selector_;
+
bool flush_input_requested_;
int accelerated_surface_route_id_;
web_contents_->GetTitle());
}
+ScopedJavaLocalRef<jstring> WebContentsAndroid::GetVisibleURL(
+ JNIEnv* env, jobject obj) const {
+ return base::android::ConvertUTF8ToJavaString(
+ env, web_contents_->GetVisibleURL().spec());
+}
+
void WebContentsAndroid::Stop(JNIEnv* env, jobject obj) {
web_contents_->Stop();
}
// Methods called from Java
base::android::ScopedJavaLocalRef<jstring> GetTitle(JNIEnv* env,
jobject obj) const;
+ base::android::ScopedJavaLocalRef<jstring> GetVisibleURL(JNIEnv* env,
+ jobject obj) const;
void Stop(JNIEnv* env, jobject obj);
private:
IPC_STRUCT_MEMBER(GURL, url)
IPC_STRUCT_MEMBER(GURL, first_party_for_cookies)
IPC_STRUCT_MEMBER(GURL, frame_url)
+ IPC_STRUCT_MEMBER(bool, allow_credentials)
IPC_STRUCT_END()
// Chrome for Android seek message sequence is:
bool /* enabled */)
// Reply to the ViewMsg_ExtractSmartClipData message.
-// TODO(juhui24.lee@samsung.com): this should be changed to a vector of structs
-// instead of encoding the data as a string which is not allowed normally. Since
-// ths is only used in Android WebView, it's allowed temporarily.
-// http://crbug.com/330872
-IPC_MESSAGE_ROUTED1(ViewHostMsg_SmartClipDataExtracted, base::string16)
+IPC_MESSAGE_ROUTED3(ViewHostMsg_SmartClipDataExtracted,
+ base::string16 /* text */,
+ base::string16 /* html */,
+ gfx::Rect /* rect */)
#elif defined(OS_MACOSX)
// Request that the browser load a font into shared memory for us.
'browser/renderer_host/ime_adapter_android.h',
'browser/renderer_host/input/gesture_event_queue.cc',
'browser/renderer_host/input/gesture_event_queue.h',
+ 'browser/renderer_host/input/gesture_text_selector.cc',
+ 'browser/renderer_host/input/gesture_text_selector.h',
'browser/renderer_host/input/input_ack_handler.h',
'browser/renderer_host/input/input_router.h',
'browser/renderer_host/input/input_router_client.h',
'public/renderer/render_frame_observer.cc',
'public/renderer/render_frame_observer.h',
'public/renderer/render_frame_observer_tracker.h',
- 'public/renderer/render_font_warmup_win.cc',
'public/renderer/render_font_warmup_win.h',
'public/renderer/render_process_observer.cc',
'public/renderer/render_process_observer.h',
'renderer/notification_provider.h',
'renderer/push_messaging_dispatcher.cc',
'renderer/push_messaging_dispatcher.h',
+ 'renderer/render_font_warmup_win.cc',
'renderer/render_frame_impl.cc',
'renderer/render_frame_impl.h',
'renderer/render_frame_proxy.cc',
'renderer/renderer_clipboard_client.h',
'renderer/renderer_date_time_picker.cc',
'renderer/renderer_date_time_picker.h',
+ 'renderer/renderer_font_platform_win.cc',
+ 'renderer/renderer_font_platform_win.h',
'renderer/renderer_main.cc',
'renderer/renderer_main_platform_delegate.h',
'renderer/renderer_main_platform_delegate_android.cc',
'browser/quota/usage_tracker_unittest.cc',
'browser/renderer_host/compositing_iosurface_transformer_mac_unittest.cc',
'browser/renderer_host/input/gesture_event_queue_unittest.cc',
+ 'browser/renderer_host/input/gesture_text_selector_unittest.cc',
'browser/renderer_host/input/input_router_impl_unittest.cc',
'browser/renderer_host/input/mock_input_ack_handler.cc',
'browser/renderer_host/input/mock_input_ack_handler.h',
--- /dev/null
+# DWrite specific changes
+per-file render_font_warmup_win.h=scottmg@chromium.org
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
* exposes the various {@link View} functionality to it.
*/
public class ContentView extends FrameLayout
- implements ContentViewCore.InternalAccessDelegate {
+ implements ContentViewCore.InternalAccessDelegate, SmartClipProvider {
+
+ private static final String TAG = "ContentView";
protected final ContentViewCore mContentViewCore;
mContentViewCore.onVisibilityChanged(changedView, visibility);
}
+ // Implements SmartClipProvider
+ @Override
+ public void extractSmartClipData(int x, int y, int width, int height) {
+ mContentViewCore.extractSmartClipData(x, y, width, height);
+ }
+
+ // Implements SmartClipProvider
+ @Override
+ public void setSmartClipResultHandler(final Handler resultHandler) {
+ if (resultHandler == null) {
+ mContentViewCore.setSmartClipDataListener(null);
+ return;
+ }
+ mContentViewCore.setSmartClipDataListener(new ContentViewCore.SmartClipDataListener() {
+ public void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
+ Bundle bundle = new Bundle();
+ bundle.putString("url", mContentViewCore.getWebContents().getVisibleUrl());
+ bundle.putString("title", mContentViewCore.getWebContents().getTitle());
+ bundle.putParcelable("rect", clipRect);
+ bundle.putString("text", text);
+ bundle.putString("html", html);
+ try {
+ Message msg = Message.obtain(resultHandler, 0);
+ msg.setData(bundle);
+ msg.sendToTarget();
+ } catch (Exception e) {
+ Log.e(TAG, "Error calling handler for smart clip data: ", e);
+ }
+ }
+ });
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////
// Start Implementation of ContentViewCore.InternalAccessDelegate //
///////////////////////////////////////////////////////////////////////////////////////////////
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.ContentObserver;
// Length of the delay (in ms) before fading in handles after the last page movement.
private static final int TEXT_HANDLE_FADE_IN_DELAY = 300;
+ // These values are obtained from Samsung.
+ // TODO(changwan): refactor SPen related code into a separate class. See
+ // http://crbug.com/398169.
+ private static final int SPEN_ACTION_DOWN = 211;
+ private static final int SPEN_ACTION_UP = 212;
+ private static final int SPEN_ACTION_MOVE = 213;
+ private static final int SPEN_ACTION_CANCEL = 214;
+ private static Boolean sIsSPenSupported;
+
// If the embedder adds a JavaScript interface object that contains an indirect reference to
// the ContentViewCore, then storing a strong ref to the interface object on the native
// side would prevent garbage collection of the ContentViewCore (as that strong ref would
* extractSmartClipData are available.
*/
public interface SmartClipDataListener {
- public void onSmartClipDataExtracted(String result);
+ public void onSmartClipDataExtracted(String text, String html, Rect clipRect);
}
private final Context mContext;
private float mCurrentTouchOffsetX;
private float mCurrentTouchOffsetY;
+ // Offsets for smart clip
+ private int mSmartClipOffsetX;
+ private int mSmartClipOffsetY;
+
/**
* Constructs a new ContentViewCore. Embedders must call initialize() after constructing
* a ContentViewCore and before using it.
// End FrameLayout overrides.
/**
+ * TODO(changwan): refactor SPen related code into a separate class. See
+ * http://crbug.com/398169.
+ * @return Whether SPen is supported on the device.
+ */
+ public static boolean isSPenSupported(Context context) {
+ if (sIsSPenSupported == null)
+ sIsSPenSupported = detectSPenSupport(context);
+ return sIsSPenSupported.booleanValue();
+ }
+
+ private static boolean detectSPenSupport(Context context) {
+ if (!"SAMSUNG".equalsIgnoreCase(Build.MANUFACTURER))
+ return false;
+
+ final FeatureInfo[] infos = context.getPackageManager().getSystemAvailableFeatures();
+ for (FeatureInfo info : infos) {
+ if ("com.sec.feature.spen_usp".equalsIgnoreCase(info.name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Convert SPen event action into normal event action.
+ * TODO(changwan): refactor SPen related code into a separate class. See
+ * http://crbug.com/398169.
+ *
+ * @param eventActionMasked Input event action. It is assumed that it is masked as the values
+ cannot be ORed.
+ * @return Event action after the conversion
+ */
+ public static int convertSPenEventAction(int eventActionMasked) {
+ // S-Pen support: convert to normal stylus event handling
+ switch (eventActionMasked) {
+ case SPEN_ACTION_DOWN:
+ return MotionEvent.ACTION_DOWN;
+ case SPEN_ACTION_UP:
+ return MotionEvent.ACTION_UP;
+ case SPEN_ACTION_MOVE:
+ return MotionEvent.ACTION_MOVE;
+ case SPEN_ACTION_CANCEL:
+ return MotionEvent.ACTION_CANCEL;
+ default:
+ return eventActionMasked;
+ }
+ }
+
+ /**
* @see View#onTouchEvent(MotionEvent)
*/
public boolean onTouchEvent(MotionEvent event) {
try {
cancelRequestToScrollFocusedEditableNodeIntoView();
- final int eventAction = event.getActionMasked();
+ int eventAction = event.getActionMasked();
+
+ if (isSPenSupported(mContext))
+ eventAction = convertSPenEventAction(eventAction);
// Only these actions have any effect on gesture detection. Other
// actions have no corresponding WebTouchEvent type and may confuse the
pointerCount > 1 ? event.getY(1) : 0,
event.getPointerId(0), pointerCount > 1 ? event.getPointerId(1) : -1,
event.getTouchMajor(), pointerCount > 1 ? event.getTouchMajor(1) : 0,
- event.getRawX(), event.getRawY());
+ event.getRawX(), event.getRawY(),
+ event.getToolType(0),
+ pointerCount > 1 ? event.getToolType(1) : MotionEvent.TOOL_TYPE_UNKNOWN,
+ event.getButtonState());
if (offset != null) offset.recycle();
return consumed;
@SuppressWarnings("unused")
@CalledByNative
+ private void showSelectionHandlesAutomatically() {
+ getSelectionHandleController().allowAutomaticShowing();
+ }
+
+ @SuppressWarnings("unused")
+ @CalledByNative
private void onSelectionBoundsChanged(Rect anchorRectDip, int anchorDir, Rect focusRectDip,
int focusDir, boolean isAnchorFirst) {
// All coordinates are in DIP.
public void extractSmartClipData(int x, int y, int width, int height) {
if (mNativeContentViewCore != 0) {
+ x += mSmartClipOffsetX;
+ y += mSmartClipOffsetY;
nativeExtractSmartClipData(mNativeContentViewCore, x, y, width, height);
}
}
+ /**
+ * Set offsets for smart clip.
+ *
+ * <p>This should be called if there is a viewport change introduced by,
+ * e.g., show and hide of a location bar.
+ *
+ * @param offsetX Offset for X position.
+ * @param offsetY Offset for Y position.
+ */
+ public void setSmartClipOffsets(int offsetX, int offsetY) {
+ mSmartClipOffsetX = offsetX;
+ mSmartClipOffsetY = offsetY;
+ }
+
@CalledByNative
- private void onSmartClipDataExtracted(String result) {
+ private void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
if (mSmartClipDataListener != null ) {
- mSmartClipDataListener.onSmartClipDataExtracted(result);
+ mSmartClipDataListener.onSmartClipDataExtracted(text, html, clipRect);
}
}
float x0, float y0, float x1, float y1,
int pointerId0, int pointerId1,
float touchMajor0, float touchMajor1,
- float rawX, float rawY);
+ float rawX, float rawY,
+ int androidToolType0, int androidToolType1, int androidButtonState);
private native int nativeSendMouseMoveEvent(
long nativeContentViewCoreImpl, long timeMs, float x, float y);
import android.media.MediaMetadataRetriever;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
+import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.Log;
context, url, cookies, userAgent);
}
+ @CalledByNative
+ private static MediaMetadata extractMediaMetadataFromFd(int fd,
+ long offset,
+ long length) {
+ return new MediaResourceGetter().extract(fd, offset, length);
+ }
+
+ @VisibleForTesting
+ MediaMetadata extract(int fd, long offset, long length) {
+ if (!androidDeviceOk(android.os.Build.MODEL, android.os.Build.VERSION.SDK_INT)) {
+ return EMPTY_METADATA;
+ }
+
+ configure(fd, offset, length);
+ return doExtractMetadata();
+ }
+
@VisibleForTesting
MediaMetadata extract(final Context context, final String url,
final String cookies, final String userAgent) {
Log.e(TAG, "Unable to configure metadata extractor");
return EMPTY_METADATA;
}
+ return doExtractMetadata();
+ }
+ private MediaMetadata doExtractMetadata() {
try {
String durationString = extractMetadata(
MediaMetadataRetriever.METADATA_KEY_DURATION);
}
@VisibleForTesting
+ void configure(int fd, long offset, long length) {
+ ParcelFileDescriptor parcelFd = ParcelFileDescriptor.adoptFd(fd);
+ try {
+ mRetriever.setDataSource(parcelFd.getFileDescriptor(),
+ offset, length);
+ } finally {
+ try {
+ parcelFd.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close file descriptor: " + e);
+ }
+ }
+ }
+
+ @VisibleForTesting
void configure(String url, Map<String,String> headers) {
mRetriever.setDataSource(url, headers);
}
--- /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.content.browser;
+
+import android.os.Handler;
+
+/**
+ * An interface to provide smart clip data when requested.
+ *
+ * NOTE: Some platforms may call these functions to extract smart clip data.
+ * Please make sure implementation of them is somewhere in the view
+ * hierarchy.
+ */
+public interface SmartClipProvider {
+ /**
+ * Initiate extraction of text, HTML, and other information for clipping puposes (smart clip)
+ * from the rectangle area defined by starting positions (x and y), and width and height.
+ */
+ void extractSmartClipData(int x, int y, int width, int height);
+
+ /**
+ * Register a handler to handle smart clip data once extraction is done.
+ */
+ void setSmartClipResultHandler(final Handler resultHandler);
+}
}
@Override
+ public String getVisibleUrl() {
+ return nativeGetVisibleURL(mNativeWebContentsAndroid);
+ }
+
+ @Override
public void stop() {
nativeStop(mNativeWebContentsAndroid);
}
private native String nativeGetTitle(long nativeWebContentsAndroid);
+ private native String nativeGetVisibleURL(long nativeWebContentsAndroid);
private native void nativeStop(long nativeWebContentsAndroid);
}
String getTitle();
/**
+ * @return The URL for the current visible page.
+ */
+ String getVisibleUrl();
+
+ /**
* Stop any pending navigation.
*/
void stop();
@SuppressLint("SdCardPath")
public class MediaResourceGetterTest extends InstrumentationTestCase {
private static final String TEST_HTTP_URL = "http://example.com";
- private static final String TEST_USER_AGENT = // Anyhting, really
+ private static final String TEST_USER_AGENT = // Anything, really
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36";
private static final String TEST_FILE_PATH = "/mnt/sdcard/test";
String mUri = null;
Map<String,String> mHeaders = null;
String mPath = null;
+ int mFd;
+ long mOffset;
+ long mLength;
// Write these before tests to configure functionality
SparseArray<String> mMetadata = null;
// Can't use a real MediaMetadataRetriever as we have no media
@Override
+ public void configure(int fd, long offset, long length) {
+ if (mThrowExceptionInConfigure) {
+ throw new RuntimeException("test exception");
+ }
+ mFd = fd;
+ mOffset = offset;
+ mLength = length;
+ }
+
+ // Can't use a real MediaMetadataRetriever as we have no media
+ @Override
public void configure(String uri, Map<String, String> headers) {
if (mThrowExceptionInConfigure) {
throw new RuntimeException("test exception");
}
@SmallTest
+ public void testExtractFromFileDescriptor_ValidMetadata() {
+ mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1");
+ mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes");
+ mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "2");
+ mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "3");
+ final MediaMetadata expected = new MediaMetadata(1, 2, 3, true);
+ int fd = 1234;
+ long offset = 1000;
+ long length = 9000;
+ assertEquals(expected, mFakeMRG.extract(fd, offset, length));
+ assertEquals(fd, mFakeMRG.mFd);
+ assertEquals(offset, mFakeMRG.mOffset);
+ assertEquals(length, mFakeMRG.mLength);
+ }
+
+ @SmallTest
public void testAndroidDeviceOk_BadModel_BadVersion() {
assertFalse(MediaResourceGetter.androidDeviceOk(
"GT-I9100", android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1));
IPC_STRUCT_TRAITS_MEMBER(use_legacy_background_size_shorthand_behavior)
IPC_STRUCT_TRAITS_MEMBER(wide_viewport_quirk)
IPC_STRUCT_TRAITS_MEMBER(use_wide_viewport)
+ IPC_STRUCT_TRAITS_MEMBER(force_zero_layout_height)
IPC_STRUCT_TRAITS_MEMBER(viewport_meta_layout_size_quirk)
IPC_STRUCT_TRAITS_MEMBER(viewport_meta_merge_content_quirk)
IPC_STRUCT_TRAITS_MEMBER(viewport_meta_non_user_scalable_quirk)
# Mac Sandbox profiles.
per-file *.sb=set noparent
per-file *.sb=jeremy@chromium.org
+
+# DirectWrite specific changes
+per-file render_font_warmup_win.cc=scottmg@chromium.org
+per-file renderer_font_platform_win.*=shrikant@chromium.org
+per-file renderer_font_platform_win.*=scottmg@chromium.org
+per-file renderer_font_platform_win.*=cpu@chromium.org
const ReadyCB& ready_cb)
: loader_failed_(false),
url_(url),
+ allow_stored_credentials_(false),
cors_mode_(cors_mode),
single_origin_(true),
ready_cb_(ready_cb) {}
CHECK(frame);
start_time_ = base::TimeTicks::Now();
+ first_party_url_ = frame->document().firstPartyForCookies();
// Prepare the request.
WebURLRequest request(url_);
options.allowCredentials = true;
options.crossOriginRequestPolicy =
WebURLLoaderOptions::CrossOriginRequestPolicyAllow;
+ allow_stored_credentials_ = true;
} else {
options.exposeAllResponseHeaders = true;
// The author header set is empty, no preflight should go ahead.
options.preflightPolicy = WebURLLoaderOptions::PreventPreflight;
options.crossOriginRequestPolicy =
WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
- if (cors_mode_ == blink::WebMediaPlayer::CORSModeUseCredentials)
+ if (cors_mode_ == blink::WebMediaPlayer::CORSModeUseCredentials) {
options.allowCredentials = true;
+ allow_stored_credentials_ = true;
+ }
}
loader.reset(frame->createAssociatedURLLoader(options));
}
single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin();
url_ = newRequest.url();
+ first_party_url_ = newRequest.firstPartyForCookies();
+ allow_stored_credentials_ = newRequest.allowStoredCredentials();
}
void MediaInfoLoader::didSendData(
base::TimeTicks::Now() - start_time_);
active_loader_.reset();
if (!ready_cb_.is_null())
- base::ResetAndReturn(&ready_cb_).Run(status);
+ base::ResetAndReturn(&ready_cb_).Run(status, url_, first_party_url_,
+ allow_stored_credentials_);
}
} // namespace content
#include "content/renderer/media/active_loader.h"
#include "third_party/WebKit/public/platform/WebMediaPlayer.h"
#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
#include "url/gurl.h"
namespace blink {
kOk,
};
+ // Callback when MediaInfoLoader finishes loading the url. Args: whether URL
+ // is successfully loaded, the final URL destination following all the
+ // redirect, the first party URL for the final destination, and whether
+ // credentials needs to be sent to the final destination.
+ typedef base::Callback<void(Status, const GURL&, const GURL&, bool)> ReadyCB;
+
// Start loading information about the given media URL.
// |url| - URL for the media resource to be loaded.
// |cors_mode| - HTML media element's crossorigin attribute.
// |ready_cb| - Called when media info has finished or failed loading.
- typedef base::Callback<void(Status)> ReadyCB;
MediaInfoLoader(
const GURL& url,
blink::WebMediaPlayer::CORSMode cors_mode,
// Only valid to call after the loader becomes ready.
bool DidPassCORSAccessCheck() const;
- void set_single_origin(bool single_origin) {
- single_origin_ = single_origin;
- }
-
private:
friend class MediaInfoLoaderTest;
bool loader_failed_;
GURL url_;
+ GURL first_party_url_;
+ bool allow_stored_credentials_;
blink::WebMediaPlayer::CORSMode cors_mode_;
bool single_origin_;
void SendResponse(
int http_status, MediaInfoLoader::Status expected_status) {
- EXPECT_CALL(*this, ReadyCallback(expected_status));
+ EXPECT_CALL(*this, ReadyCallback(expected_status, _, _, _));
EXPECT_CALL(*url_loader_, cancel());
WebURLResponse response(gurl_);
}
void FailLoad() {
- EXPECT_CALL(*this, ReadyCallback(MediaInfoLoader::kFailed));
+ EXPECT_CALL(*this, ReadyCallback(
+ MediaInfoLoader::kFailed, _, _, _));
loader_->didFail(url_loader_, WebURLError());
}
- MOCK_METHOD1(ReadyCallback, void(MediaInfoLoader::Status));
+ MOCK_METHOD4(ReadyCallback,
+ void(MediaInfoLoader::Status, const GURL&, const GURL&, bool));
protected:
GURL gurl_;
const GURL& url,
const GURL& first_party_for_cookies,
int demuxer_client_id,
- const GURL& frame_url) {
-
+ const GURL& frame_url,
+ bool allow_credentials) {
MediaPlayerHostMsg_Initialize_Params media_player_params;
media_player_params.type = type;
media_player_params.player_id = player_id;
media_player_params.url = url;
media_player_params.first_party_for_cookies = first_party_for_cookies;
media_player_params.frame_url = frame_url;
+ media_player_params.allow_credentials = allow_credentials;
Send(new MediaPlayerHostMsg_Initialize(routing_id(), media_player_params));
}
const GURL& url,
const GURL& first_party_for_cookies,
int demuxer_client_id,
- const GURL& frame_url);
+ const GURL& frame_url,
+ bool allow_credentials);
// Starts the player.
void Start(int player_id);
#include <algorithm>
#include <limits>
+#include "base/android/build_info.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "ui/gfx/image/image.h"
static const uint32 kGLTextureExternalOES = 0x8D65;
+static const int kSDKVersionToSupportSecurityOriginCheck = 20;
using blink::WebMediaPlayer;
using blink::WebSize;
texture_id_(0),
stream_id_(0),
is_playing_(false),
- playing_started_(false),
needs_establish_peer_(true),
stream_texture_proxy_initialized_(false),
has_size_info_(false),
- has_media_metadata_(false),
- has_media_info_(false),
stream_texture_factory_(factory),
needs_external_surface_(false),
video_frame_provider_client_(NULL),
is_remote_(false),
media_log_(media_log),
web_cdm_(NULL),
+ allow_stored_credentials_(false),
weak_factory_(this) {
DCHECK(player_manager_);
DCHECK(cdm_manager_);
return;
}
- has_media_metadata_ = false;
- has_media_info_ = false;
-
+ url_ = url;
int demuxer_client_id = 0;
if (player_type_ != MEDIA_PLAYER_TYPE_URL) {
- has_media_info_ = true;
-
RendererDemuxerAndroid* demuxer =
RenderThreadImpl::current()->renderer_demuxer();
demuxer_client_id = demuxer->GetNextDemuxerClientID();
weak_factory_.GetWeakPtr()),
base::Bind(&WebMediaPlayerAndroid::OnDurationChanged,
weak_factory_.GetWeakPtr()));
+ InitializePlayer(url_, frame_->document().firstPartyForCookies(),
+ true, demuxer_client_id);
}
} else {
info_loader_.reset(
cors_mode,
base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo,
weak_factory_.GetWeakPtr())));
- // TODO(qinmin): The url might be redirected when android media player
- // requests the stream. As a result, we cannot guarantee there is only
- // a single origin. Remove the following line when b/12573548 is fixed.
- // Check http://crbug.com/334204.
- info_loader_->set_single_origin(false);
info_loader_->Start(frame_);
}
-
- url_ = url;
- GURL first_party_url = frame_->document().firstPartyForCookies();
- player_manager_->Initialize(
- player_type_, player_id_, url, first_party_url, demuxer_client_id,
- frame_->document().url());
-
- if (player_manager_->ShouldEnterFullscreen(frame_))
- player_manager_->EnterFullscreen(player_id_, frame_);
-
+
UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing);
}
-void WebMediaPlayerAndroid::DidLoadMediaInfo(MediaInfoLoader::Status status) {
+void WebMediaPlayerAndroid::DidLoadMediaInfo(
+ MediaInfoLoader::Status status,
+ const GURL& redirected_url,
+ const GURL& first_party_for_cookies,
+ bool allow_stored_credentials) {
DCHECK(!media_source_delegate_);
if (status == MediaInfoLoader::kFailed) {
info_loader_.reset();
return;
}
- has_media_info_ = true;
- if (has_media_metadata_ &&
- ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
- UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
- UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
- }
- // Android doesn't start fetching resources until an implementation-defined
- // event (e.g. playback request) occurs. Sets the network state to IDLE
- // if play is not requested yet.
- if (!playing_started_)
- UpdateNetworkState(WebMediaPlayer::NetworkStateIdle);
+ InitializePlayer(
+ redirected_url, first_party_for_cookies, allow_stored_credentials, 0);
+
+ UpdateNetworkState(WebMediaPlayer::NetworkStateIdle);
}
void WebMediaPlayerAndroid::play() {
player_manager_->Start(player_id_);
UpdatePlayingState(true);
UpdateNetworkState(WebMediaPlayer::NetworkStateLoading);
- playing_started_ = true;
}
void WebMediaPlayerAndroid::pause() {
}
bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const {
- if (info_loader_)
- return info_loader_->HasSingleOrigin();
- // The info loader may have failed.
- if (player_type_ == MEDIA_PLAYER_TYPE_URL)
+ if (player_type_ != MEDIA_PLAYER_TYPE_URL)
+ return true;
+
+ if (!info_loader_ || !info_loader_->HasSingleOrigin())
return false;
- return true;
+
+ // TODO(qinmin): The url might be redirected when android media player
+ // requests the stream. As a result, we cannot guarantee there is only
+ // a single origin. Only if the HTTP request was made without credentials,
+ // we will honor the return value from HasSingleSecurityOriginInternal()
+ // in pre-L android versions.
+ // Check http://crbug.com/334204.
+ if (!allow_stored_credentials_)
+ return true;
+
+ return base::android::BuildInfo::GetInstance()->sdk_int() >=
+ kSDKVersionToSupportSecurityOriginCheck;
}
bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const {
}
}
- has_media_metadata_ = true;
- if (has_media_info_ &&
- ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
+ if (ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) {
UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData);
}
"the RenderFrame goes away.";
}
+void WebMediaPlayerAndroid::InitializePlayer(
+ const GURL& url,
+ const GURL& first_party_for_cookies,
+ bool allow_stored_credentials,
+ int demuxer_client_id) {
+ allow_stored_credentials_ = allow_stored_credentials;
+ player_manager_->Initialize(
+ player_type_, player_id_, url, first_party_for_cookies, demuxer_client_id,
+ frame_->document().url(), allow_stored_credentials);
+ if (player_manager_->ShouldEnterFullscreen(frame_))
+ player_manager_->EnterFullscreen(player_id_, frame_);
+}
+
void WebMediaPlayerAndroid::Pause(bool is_media_related_action) {
player_manager_->Pause(player_id_, is_media_related_action);
UpdatePlayingState(false);
void SetNeedsEstablishPeer(bool needs_establish_peer);
private:
+ void InitializePlayer(const GURL& url,
+ const GURL& first_party_for_cookies,
+ bool allowed_stored_credentials,
+ int demuxer_client_id);
void Pause(bool is_media_related_action);
void DrawRemotePlaybackText(const std::string& remote_playback_message);
void ReallocateVideoFrame();
void SetCurrentFrameInternal(scoped_refptr<media::VideoFrame>& frame);
- void DidLoadMediaInfo(MediaInfoLoader::Status status);
+ void DidLoadMediaInfo(MediaInfoLoader::Status status,
+ const GURL& redirected_url,
+ const GURL& first_party_for_cookies,
+ bool allow_stored_credentials);
bool IsKeySystemSupported(const std::string& key_system);
// Actually do the work for generateKeyRequest/addKey so they can easily
// Whether the mediaplayer is playing.
bool is_playing_;
- // Whether the mediaplayer has already started playing.
- bool playing_started_;
-
// Whether media player needs to re-establish the surface texture peer.
bool needs_establish_peer_;
// Whether the video size info is available.
bool has_size_info_;
- // Whether the video metadata and info are available.
- bool has_media_metadata_;
- bool has_media_info_;
-
// Object for allocating stream textures.
scoped_refptr<StreamTextureFactory> stream_texture_factory_;
// player_manager_->SetCdm() directly.
media::DecryptorReadyCB decryptor_ready_cb_;
+ // Whether stored credentials are allowed to be passed to the server.
+ bool allow_stored_credentials_;
+
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<WebMediaPlayerAndroid> weak_factory_;
--- /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 "content/public/renderer/render_font_warmup_win.h"
+
+#include <dwrite.h>
+
+#include "base/debug/alias.h"
+#include "base/logging.h"
+#include "base/win/iat_patch_function.h"
+#include "base/win/windows_version.h"
+#include "content/renderer/renderer_font_platform_win.h"
+#include "third_party/WebKit/public/web/win/WebFontRendering.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/ports/SkFontMgr.h"
+#include "third_party/skia/include/ports/SkTypeface_win.h"
+
+namespace content {
+
+namespace {
+
+SkFontMgr* g_warmup_fontmgr = NULL;
+
+base::win::IATPatchFunction g_iat_patch_open_sc_manager;
+base::win::IATPatchFunction g_iat_patch_close_service_handle;
+base::win::IATPatchFunction g_iat_patch_open_service;
+base::win::IATPatchFunction g_iat_patch_start_service;
+base::win::IATPatchFunction g_iat_patch_nt_connect_port;
+
+// These are from ntddk.h
+#if !defined(STATUS_ACCESS_DENIED)
+#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
+#endif
+
+typedef LONG NTSTATUS;
+
+SC_HANDLE WINAPI OpenSCManagerWPatch(const wchar_t* machine_name,
+ const wchar_t* database_name,
+ DWORD access_mask) {
+ ::SetLastError(0);
+ return reinterpret_cast<SC_HANDLE>(0xdeadbeef);
+}
+
+SC_HANDLE WINAPI OpenServiceWPatch(SC_HANDLE sc_manager,
+ const wchar_t* service_name,
+ DWORD access_mask) {
+ ::SetLastError(0);
+ return reinterpret_cast<SC_HANDLE>(0xdeadbabe);
+}
+
+BOOL WINAPI CloseServiceHandlePatch(SC_HANDLE service_handle) {
+ if (service_handle != reinterpret_cast<SC_HANDLE>(0xdeadbabe) &&
+ service_handle != reinterpret_cast<SC_HANDLE>(0xdeadbeef))
+ CHECK(false);
+ ::SetLastError(0);
+ return TRUE;
+}
+
+BOOL WINAPI StartServiceWPatch(SC_HANDLE service,
+ DWORD args,
+ const wchar_t** arg_vectors) {
+ if (service != reinterpret_cast<SC_HANDLE>(0xdeadbabe))
+ CHECK(false);
+ ::SetLastError(ERROR_ACCESS_DENIED);
+ return FALSE;
+}
+
+NTSTATUS WINAPI NtALpcConnectPortPatch(HANDLE* port_handle,
+ void* port_name,
+ void* object_attribs,
+ void* port_attribs,
+ DWORD flags,
+ void* server_sid,
+ void* message,
+ DWORD* buffer_length,
+ void* out_message_attributes,
+ void* in_message_attributes,
+ void* time_out) {
+ return STATUS_ACCESS_DENIED;
+}
+
+// Directwrite connects to the font cache service to retrieve information about
+// fonts installed on the system etc. This works well outside the sandbox and
+// within the sandbox as long as the lpc connection maintained by the current
+// process with the font cache service remains valid. It appears that there
+// are cases when this connection is dropped after which directwrite is unable
+// to connect to the font cache service which causes problems with characters
+// disappearing.
+// Directwrite has fallback code to enumerate fonts if it is unable to connect
+// to the font cache service. We need to intercept the following APIs to
+// ensure that it does not connect to the font cache service.
+// NtALpcConnectPort
+// OpenSCManagerW
+// OpenServiceW
+// StartServiceW
+// CloseServiceHandle.
+// These are all IAT patched.
+void PatchServiceManagerCalls() {
+ static bool is_patched = false;
+ if (is_patched)
+ return;
+ const char* service_provider_dll =
+ (base::win::GetVersion() >= base::win::VERSION_WIN8 ?
+ "api-ms-win-service-management-l1-1-0.dll" : "advapi32.dll");
+
+ is_patched = true;
+
+ DWORD patched = g_iat_patch_open_sc_manager.Patch(L"dwrite.dll",
+ service_provider_dll, "OpenSCManagerW", OpenSCManagerWPatch);
+ DCHECK(patched == 0);
+
+ patched = g_iat_patch_close_service_handle.Patch(L"dwrite.dll",
+ service_provider_dll, "CloseServiceHandle", CloseServiceHandlePatch);
+ DCHECK(patched == 0);
+
+ patched = g_iat_patch_open_service.Patch(L"dwrite.dll",
+ service_provider_dll, "OpenServiceW", OpenServiceWPatch);
+ DCHECK(patched == 0);
+
+ patched = g_iat_patch_start_service.Patch(L"dwrite.dll",
+ service_provider_dll, "StartServiceW", StartServiceWPatch);
+ DCHECK(patched == 0);
+
+ patched = g_iat_patch_nt_connect_port.Patch(L"dwrite.dll",
+ "ntdll.dll", "NtAlpcConnectPort", NtALpcConnectPortPatch);
+ DCHECK(patched == 0);
+}
+
+// Windows-only DirectWrite support. These warm up the DirectWrite paths
+// before sandbox lock down to allow Skia access to the Font Manager service.
+void CreateDirectWriteFactory(IDWriteFactory** factory) {
+ typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc;
+ HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll");
+ // TODO(scottmg): Temporary code to track crash in http://crbug.com/387867.
+ if (!dwrite_dll) {
+ DWORD load_library_get_last_error = GetLastError();
+ base::debug::Alias(&dwrite_dll);
+ base::debug::Alias(&load_library_get_last_error);
+ CHECK(false);
+ }
+
+ PatchServiceManagerCalls();
+
+ DWriteCreateFactoryProc dwrite_create_factory_proc =
+ reinterpret_cast<DWriteCreateFactoryProc>(
+ GetProcAddress(dwrite_dll, "DWriteCreateFactory"));
+ // TODO(scottmg): Temporary code to track crash in http://crbug.com/387867.
+ if (!dwrite_create_factory_proc) {
+ DWORD get_proc_address_get_last_error = GetLastError();
+ base::debug::Alias(&dwrite_create_factory_proc);
+ base::debug::Alias(&get_proc_address_get_last_error);
+ CHECK(false);
+ }
+ CHECK(SUCCEEDED(
+ dwrite_create_factory_proc(DWRITE_FACTORY_TYPE_ISOLATED,
+ __uuidof(IDWriteFactory),
+ reinterpret_cast<IUnknown**>(factory))));
+}
+
+HRESULT STDMETHODCALLTYPE StubFontCollection(IDWriteFactory* factory,
+ IDWriteFontCollection** col,
+ BOOL checkUpdates) {
+ // We always return pre-created font collection from here.
+ IDWriteFontCollection* custom_collection = GetCustomFontCollection(factory);
+ DCHECK(custom_collection != NULL);
+ *col = custom_collection;
+ return S_OK;
+}
+
+void PatchDWriteFactory(IDWriteFactory* factory) {
+ const unsigned int kGetSystemFontCollectionVTableIndex = 3;
+
+ PROC* vtable = *reinterpret_cast<PROC**>(factory);
+ PROC* function_ptr = &vtable[kGetSystemFontCollectionVTableIndex];
+ void* stub_function = &StubFontCollection;
+ base::win::ModifyCode(function_ptr, &stub_function, sizeof(PROC));
+}
+
+} // namespace
+
+void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) {
+ SkPaint paint_warmup;
+ paint_warmup.setTypeface(typeface);
+ wchar_t glyph = L'S';
+ paint_warmup.measureText(&glyph, 2);
+}
+
+SkFontMgr* GetPreSandboxWarmupFontMgr() {
+ if (!g_warmup_fontmgr) {
+ IDWriteFactory* factory;
+ CreateDirectWriteFactory(&factory);
+
+ IDWriteFontCollection* collection = GetCustomFontCollection(factory);
+
+ PatchDWriteFactory(factory);
+
+ blink::WebFontRendering::setDirectWriteFactory(factory);
+ g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory);
+ }
+ return g_warmup_fontmgr;
+}
+
+} // namespace content
}
void RenderViewImpl::OnExtractSmartClipData(const gfx::Rect& rect) {
+ blink::WebString clip_text;
+ blink::WebString clip_html;
+ blink::WebRect clip_rect;
+ webview()->extractSmartClipData(rect, clip_text, clip_html, clip_rect);
Send(new ViewHostMsg_SmartClipDataExtracted(
- routing_id_, webview()->getSmartClipData(rect)));
+ routing_id_, clip_text, clip_html, clip_rect));
}
} // namespace content
--- /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 "content/renderer/renderer_font_platform_win.h"
+
+#include <dwrite.h>
+#include <string>
+#include <vector>
+#include <wrl/implements.h>
+#include <wrl/wrappers/corewrappers.h>
+
+#include "base/debug/alias.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/path_service.h"
+#include "base/time/time.h"
+#include "base/win/iat_patch_function.h"
+#include "base/win/registry.h"
+#include "base/win/scoped_comptr.h"
+
+namespace {
+
+namespace mswr = Microsoft::WRL;
+namespace mswrw = Microsoft::WRL::Wrappers;
+
+class FontCollectionLoader
+ : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
+ IDWriteFontCollectionLoader> {
+ public:
+ // IDWriteFontCollectionLoader methods.
+ virtual HRESULT STDMETHODCALLTYPE
+ CreateEnumeratorFromKey(IDWriteFactory* factory,
+ void const* key,
+ UINT32 key_size,
+ IDWriteFontFileEnumerator** file_enumerator);
+
+ static HRESULT Initialize(IDWriteFactory* factory);
+
+ UINT32 GetFontMapSize();
+
+ std::wstring GetFontNameFromKey(UINT32 idx);
+
+ bool LoadFontListFromRegistry();
+
+ FontCollectionLoader() {};
+ virtual ~FontCollectionLoader() {};
+
+ private:
+ mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
+
+ std::vector<std::wstring> reg_fonts_;
+};
+
+mswr::ComPtr<FontCollectionLoader> g_font_loader;
+
+class FontFileStream
+ : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
+ IDWriteFontFileStream> {
+ public:
+ // IDWriteFontFileStream methods.
+ virtual HRESULT STDMETHODCALLTYPE
+ ReadFileFragment(void const** fragment_start,
+ UINT64 file_offset,
+ UINT64 fragment_size,
+ void** context) {
+ if (!memory_.get() || !memory_->IsValid())
+ return E_FAIL;
+
+ *fragment_start = static_cast<BYTE const*>(memory_->data()) +
+ static_cast<size_t>(file_offset);
+ *context = NULL;
+ return S_OK;
+ }
+
+ virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* context) {}
+
+ virtual HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* file_size) {
+ if (!memory_.get() || !memory_->IsValid())
+ return E_FAIL;
+
+ *file_size = memory_->length();
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(UINT64* last_write_time) {
+ if (!memory_.get() || !memory_->IsValid())
+ return E_FAIL;
+
+ // According to MSDN article http://goo.gl/rrSYzi the "last modified time"
+ // is used by DirectWrite font selection algorithms to determine whether
+ // one font resource is more up to date than another one.
+ // So by returning 0 we are assuming that it will treat all fonts to be
+ // equally up to date.
+ // TODO(shrikant): We should further investigate this.
+ *last_write_time = 0;
+ return S_OK;
+ }
+
+ FontFileStream::FontFileStream() : font_key_(0) {
+ };
+
+ 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());
+ memory_.reset(new base::MemoryMappedFile());
+
+ // Put some debug information on stack.
+ WCHAR font_name[256];
+ path.value().copy(font_name, arraysize(font_name));
+ base::debug::Alias(font_name);
+
+ if (!memory_->Initialize(path)) {
+ memory_.reset();
+ return E_FAIL;
+ }
+
+ font_key_ = font_key;
+ return S_OK;
+ }
+
+ virtual ~FontFileStream() {}
+
+ UINT32 font_key_;
+ scoped_ptr<base::MemoryMappedFile> memory_;
+};
+
+class FontFileLoader
+ : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
+ IDWriteFontFileLoader> {
+ public:
+ // IDWriteFontFileLoader methods.
+ virtual HRESULT STDMETHODCALLTYPE
+ CreateStreamFromKey(void const* ref_key,
+ UINT32 ref_key_size,
+ IDWriteFontFileStream** stream) {
+ if (ref_key_size != sizeof(UINT32))
+ return E_FAIL;
+
+ UINT32 font_key = *static_cast<const UINT32*>(ref_key);
+ mswr::ComPtr<FontFileStream> font_stream;
+ HRESULT hr = mswr::MakeAndInitialize<FontFileStream>(&font_stream,
+ font_key);
+ if (SUCCEEDED(hr)) {
+ *stream = font_stream.Detach();
+ return S_OK;
+ }
+ return E_FAIL;
+ }
+
+ FontFileLoader() {}
+ virtual ~FontFileLoader() {}
+};
+
+class FontFileEnumerator
+ : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
+ IDWriteFontFileEnumerator> {
+ public:
+ // IDWriteFontFileEnumerator methods.
+ virtual HRESULT STDMETHODCALLTYPE MoveNext(BOOL* has_current_file) {
+ *has_current_file = FALSE;
+
+ if (current_file_)
+ current_file_.ReleaseAndGetAddressOf();
+
+ if (font_idx_ < g_font_loader->GetFontMapSize()) {
+ HRESULT hr =
+ factory_->CreateCustomFontFileReference(&font_idx_,
+ sizeof(UINT32),
+ file_loader_.Get(),
+ current_file_.GetAddressOf());
+ DCHECK(SUCCEEDED(hr));
+ *has_current_file = TRUE;
+ font_idx_++;
+ }
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE
+ GetCurrentFontFile(IDWriteFontFile** font_file) {
+ if (!current_file_) {
+ *font_file = NULL;
+ return E_FAIL;
+ }
+
+ *font_file = current_file_.Detach();
+ return S_OK;
+ }
+
+ FontFileEnumerator(const void* keys,
+ UINT32 buffer_size,
+ IDWriteFactory* factory,
+ IDWriteFontFileLoader* file_loader)
+ : factory_(factory), file_loader_(file_loader), font_idx_(0) {}
+
+ virtual ~FontFileEnumerator() {}
+
+ mswr::ComPtr<IDWriteFactory> factory_;
+ mswr::ComPtr<IDWriteFontFile> current_file_;
+ mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
+ UINT32 font_idx_;
+};
+
+// IDWriteFontCollectionLoader methods.
+HRESULT STDMETHODCALLTYPE FontCollectionLoader::CreateEnumeratorFromKey(
+ IDWriteFactory* factory,
+ void const* key,
+ UINT32 key_size,
+ IDWriteFontFileEnumerator** file_enumerator) {
+ *file_enumerator = mswr::Make<FontFileEnumerator>(
+ key, key_size, factory, file_loader_.Get()).Detach();
+ return S_OK;
+}
+
+// static
+HRESULT FontCollectionLoader::Initialize(IDWriteFactory* factory) {
+ DCHECK(g_font_loader == NULL);
+
+ g_font_loader = mswr::Make<FontCollectionLoader>();
+ if (!g_font_loader) {
+ DCHECK(FALSE);
+ return E_FAIL;
+ }
+
+ CHECK(g_font_loader->LoadFontListFromRegistry());
+
+ g_font_loader->file_loader_ = mswr::Make<FontFileLoader>().Detach();
+
+ factory->RegisterFontFileLoader(g_font_loader->file_loader_.Get());
+ factory->RegisterFontCollectionLoader(g_font_loader.Get());
+
+ return S_OK;
+}
+
+UINT32 FontCollectionLoader::GetFontMapSize() {
+ return reg_fonts_.size();
+}
+
+std::wstring FontCollectionLoader::GetFontNameFromKey(UINT32 idx) {
+ DCHECK(idx < reg_fonts_.size());
+ return reg_fonts_[idx];
+}
+
+bool FontCollectionLoader::LoadFontListFromRegistry() {
+ const wchar_t kFontsRegistry[] =
+ L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
+ CHECK(reg_fonts_.empty());
+ base::win::RegKey regkey;
+ if (regkey.Open(HKEY_LOCAL_MACHINE, kFontsRegistry, KEY_READ) !=
+ ERROR_SUCCESS) {
+ return false;
+ }
+
+ std::wstring name;
+ std::wstring value;
+ for (DWORD idx = 0; idx < regkey.GetValueCount(); idx++) {
+ if (regkey.GetValueNameAt(idx, &name) == ERROR_SUCCESS &&
+ regkey.ReadValue(name.c_str(), &value) == ERROR_SUCCESS) {
+ base::FilePath path(value.c_str());
+ // We need to check if file name is the only component that exists,
+ // 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());
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+namespace content {
+
+mswr::ComPtr<IDWriteFontCollection> g_font_collection;
+
+IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory) {
+ if (g_font_collection.Get() != NULL)
+ return g_font_collection.Get();
+
+ base::TimeTicks start_tick = base::TimeTicks::Now();
+
+ FontCollectionLoader::Initialize(factory);
+
+ HRESULT 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);
+
+ CHECK(SUCCEEDED(hr));
+ CHECK(g_font_collection.Get() != NULL);
+
+ return g_font_collection.Get();
+}
+
+} // namespace content
--- /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 CHROME_RENDERER_RENDERER_FONT_PLATFORM_WIN_H_
+#define CHROME_RENDERER_RENDERER_FONT_PLATFORM_WIN_H_
+
+struct IDWriteFactory;
+struct IDWriteFontCollection;
+
+namespace content {
+
+IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory);
+
+} // namespace content
+
+#endif // CHROME_RENDERER_RENDERER_FONT_PLATFORM_WIN_H_
android:permission="org.chromium.content_shell.permission.SANDBOX"
android:isolatedProcess="true"
android:exported="false" />
+ <meta-data android:name="org.chromium.content.browser.SMART_CLIP_PROVIDER"
+ android:value="org.chromium.content.browser.SmartClipProvider" />
</application>
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />
leveldb::WriteOptions write_options;
write_options.sync = true;
- leveldb::Status status = db_->Delete(write_options, MakeSlice(app_id));
+ leveldb::Status status =
+ db_->Delete(write_options, MakeSlice(MakeRegistrationKey(app_id)));
if (status.ok()) {
foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
return;
load_result->registrations["app2"]->sender_ids[0]);
EXPECT_EQ(registration2->sender_ids[1],
load_result->registrations["app2"]->sender_ids[1]);
+
+ gcm_store->RemoveRegistration(
+ "app2",
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ gcm_store = BuildGCMStore().Pass();
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ ASSERT_EQ(1u, load_result->registrations.size());
+ ASSERT_TRUE(load_result->registrations.find("app1") !=
+ load_result->registrations.end());
+ EXPECT_EQ(registration1->registration_id,
+ load_result->registrations["app1"]->registration_id);
+ ASSERT_EQ(1u, load_result->registrations["app1"]->sender_ids.size());
+ EXPECT_EQ(registration1->sender_ids[0],
+ load_result->registrations["app1"]->sender_ids[0]);
}
// Verify saving some incoming messages, reopening the directory, and then
1.0f);
state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
clear_bits |= GL_COLOR_BUFFER_BIT;
- framebuffer->PrepareDrawBuffersForClear();
+ if (feature_info_->feature_flags().ext_draw_buffers)
+ framebuffer->PrepareDrawBuffersForClear();
}
if (framebuffer->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT) ||
state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false);
glClear(clear_bits);
- if ((clear_bits | GL_COLOR_BUFFER_BIT) != 0)
+ if ((clear_bits | GL_COLOR_BUFFER_BIT) != 0 &&
+ feature_info_->feature_flags().ext_draw_buffers)
framebuffer->RestoreDrawBuffersAfterClear();
framebuffer_manager()->MarkAttachmentsAsCleared(
{
"name": "gpu driver bug list",
// Please update the version number whenever you change this file.
- "version": "6.7",
+ "version": "6.10",
"entries": [
{
"id": 1,
"features": [
"disable_d3d11"
]
+ },
+ {
+ "id": 87,
+ "description": "Disable use of Direct3D 11 on older AMD drivers",
+ "cr_bugs": [402134],
+ "os": {
+ "type": "win"
+ },
+ "vendor_id": "0x1002",
+ "driver_date": {
+ "op": "<",
+ "value": "2011.1"
+ },
+ "features": [
+ "disable_d3d11"
+ ]
}
]
}
CHECK_GE(pa_stream_begin_write(pa_stream_, &buffer, &bytes_to_fill), 0);
CHECK_EQ(bytes_to_fill, static_cast<size_t>(params_.GetBytesPerBuffer()));
+ // NOTE: |bytes_to_fill| may be larger than |requested_bytes| now, this is
+ // okay since pa_stream_begin_write() is the authoritative source on how
+ // much can be written.
+
int frames_filled = 0;
if (source_callback_) {
- uint32 hardware_delay = pulse::GetHardwareLatencyInBytes(
- pa_stream_, params_.sample_rate(),
- params_.GetBytesPerFrame());
+ const uint32 hardware_delay = pulse::GetHardwareLatencyInBytes(
+ pa_stream_, params_.sample_rate(), params_.GetBytesPerFrame());
frames_filled = source_callback_->OnMoreData(
audio_bus_.get(), AudioBuffersState(0, hardware_delay));
- }
- // Zero any unfilled data so it plays back as silence.
- if (frames_filled < audio_bus_->frames()) {
- audio_bus_->ZeroFramesPartial(
- frames_filled, audio_bus_->frames() - frames_filled);
- }
+ // Zero any unfilled data so it plays back as silence.
+ if (frames_filled < audio_bus_->frames()) {
+ audio_bus_->ZeroFramesPartial(
+ frames_filled, audio_bus_->frames() - frames_filled);
+ }
- // Note: If this ever changes to output raw float the data must be clipped
- // and sanitized since it may come from an untrusted source such as NaCl.
- audio_bus_->Scale(volume_);
- audio_bus_->ToInterleaved(
- audio_bus_->frames(), params_.bits_per_sample() / 8, buffer);
+ // Note: If this ever changes to output raw float the data must be clipped
+ // and sanitized since it may come from an untrusted source such as NaCl.
+ audio_bus_->Scale(volume_);
+ audio_bus_->ToInterleaved(
+ audio_bus_->frames(), params_.bits_per_sample() / 8, buffer);
+ } else {
+ memset(buffer, 0, bytes_to_fill);
+ }
if (pa_stream_write(pa_stream_, buffer, bytes_to_fill, NULL, 0LL,
PA_SEEK_RELATIVE) < 0) {
}
}
+ // NOTE: As mentioned above, |bytes_remaining| may be negative after this.
bytes_remaining -= bytes_to_fill;
+
+ // Despite telling Pulse to only request certain buffer sizes, it will not
+ // always obey. In these cases we need to avoid back to back reads from
+ // the renderer as it won't have time to complete the request.
+ //
+ // We can't defer the callback as Pulse will never call us again until we've
+ // satisfied writing the requested number of bytes.
+ //
+ // TODO(dalecurtis): It might be worth choosing the sleep duration based on
+ // the hardware latency return above. Watch http://crbug.com/366433 to see
+ // if a more complicated wait process is necessary. We may also need to see
+ // if a PostDelayedTask should be used here to avoid blocking the PulseAudio
+ // command thread.
+ if (source_callback_ && bytes_remaining > 0)
+ base::PlatformThread::Sleep(params_.GetBufferDuration() / 4);
}
}
// |minreq| bytes. |tlength| should be a multiple of |minreq|; too low and
// Pulse will issue callbacks way too fast, too high and we don't get
// callbacks frequently enough.
+ //
+ // Setting |minreq| to the exact buffer size leads to more callbacks than
+ // necessary, so we've clipped it to half the buffer size. Regardless of the
+ // requested amount, we'll always fill |params.GetBytesPerBuffer()| though.
pa_buffer_attr pa_buffer_attributes;
pa_buffer_attributes.maxlength = static_cast<uint32_t>(-1);
- pa_buffer_attributes.minreq = params.GetBytesPerBuffer();
+ pa_buffer_attributes.minreq = params.GetBytesPerBuffer() / 2;
pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1);
pa_buffer_attributes.tlength = params.GetBytesPerBuffer() * 3;
pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1);
STDMETHODIMP AudioDeviceListenerWin::OnDeviceStateChanged(LPCWSTR device_id,
DWORD new_state) {
- if (new_state != DEVICE_STATE_ACTIVE && new_state != DEVICE_STATE_NOTPRESENT)
- return S_OK;
-
base::SystemMonitor* monitor = base::SystemMonitor::Get();
if (monitor)
monitor->ProcessDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE);
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Base64InputStream;
if (hideUrlLog) headersMap.put("x-hide-urls-from-log", "true");
if (!TextUtils.isEmpty(cookies)) headersMap.put("Cookie", cookies);
if (!TextUtils.isEmpty(userAgent)) headersMap.put("User-Agent", userAgent);
+ // The security origin check is enforced for devices above K. For devices below K,
+ // only anonymous media HTTP request (no cookies) may be considered same-origin.
+ // Note that if the server rejects the request we must not consider it same-origin.
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
+ headersMap.put("allow-cross-domain-redirect", "false");
+ }
try {
if (sResourceLoadFilter != null &&
sResourceLoadFilter.shouldOverrideResourceLoading(
}
@CalledByNative
+ protected boolean setDataSourceFromFd(int fd, long offset, long length) {
+ try {
+ ParcelFileDescriptor parcelFd = ParcelFileDescriptor.adoptFd(fd);
+ getLocalPlayer().setDataSource(parcelFd.getFileDescriptor(), offset, length);
+ parcelFd.close();
+ return true;
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to set data source from file descriptor: " + e);
+ return false;
+ }
+ }
+
+ @CalledByNative
protected boolean setDataUriDataSource(final Context context, final String url) {
if (mLoadDataUriTask != null) {
mLoadDataUriTask.cancel(true);
#include "jni/MediaPlayerBridge_jni.h"
#include "media/base/android/media_player_manager.h"
#include "media/base/android/media_resource_getter.h"
+#include "media/base/android/media_url_interceptor.h"
using base::android::ConvertUTF8ToJavaString;
using base::android::ScopedJavaLocalRef;
MediaPlayerManager* manager,
const RequestMediaResourcesCB& request_media_resources_cb,
const ReleaseMediaResourcesCB& release_media_resources_cb,
- const GURL& frame_url)
+ const GURL& frame_url,
+ bool allow_credentials)
: MediaPlayerAndroid(player_id,
manager,
request_media_resources_cb,
can_seek_backward_(true),
is_surface_in_use_(false),
volume_(-1.0),
+ allow_credentials_(allow_credentials),
weak_factory_(this) {
listener_.reset(new MediaPlayerListener(base::MessageLoopProxy::current(),
weak_factory_.GetWeakPtr()));
return;
}
+ // Start extracting the metadata immediately if the request is anonymous.
+ // Otherwise, wait for user credentials to be retrieved first.
+ if (!allow_credentials_) {
+ ExtractMediaMetadata(url_.spec());
+ return;
+ }
+
resource_getter->GetCookies(url_,
first_party_for_cookies_,
base::Bind(&MediaPlayerBridge::OnCookiesRetrieved,
JNIEnv* env = base::android::AttachCurrentThread();
CHECK(env);
- // Create a Java String for the URL.
- ScopedJavaLocalRef<jstring> j_url_string = ConvertUTF8ToJavaString(env, url);
- ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
- env, cookies_);
- ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
- env, user_agent_);
+ int fd;
+ int64 offset;
+ int64 size;
+ if (InterceptMediaUrl(url, &fd, &offset, &size)) {
+ if (!Java_MediaPlayerBridge_setDataSourceFromFd(
+ env, j_media_player_bridge_.obj(), fd, offset, size)) {
+ OnMediaError(MEDIA_ERROR_FORMAT);
+ return;
+ }
+ } else {
+ // Create a Java String for the URL.
+ ScopedJavaLocalRef<jstring> j_url_string =
+ ConvertUTF8ToJavaString(env, url);
+
+ jobject j_context = base::android::GetApplicationContext();
+ DCHECK(j_context);
+
+ const std::string data_uri_prefix("data:");
+ if (StartsWithASCII(url, data_uri_prefix, true)) {
+ if (!Java_MediaPlayerBridge_setDataUriDataSource(
+ env, j_media_player_bridge_.obj(), j_context, j_url_string.obj())) {
+ OnMediaError(MEDIA_ERROR_FORMAT);
+ }
+ return;
+ }
- jobject j_context = base::android::GetApplicationContext();
- DCHECK(j_context);
+ ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(
+ env, cookies_);
+ ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString(
+ env, user_agent_);
- const std::string data_uri_prefix("data:");
- if (StartsWithASCII(url, data_uri_prefix, true)) {
- if (!Java_MediaPlayerBridge_setDataUriDataSource(
- env, j_media_player_bridge_.obj(), j_context, j_url_string.obj())) {
+ if (!Java_MediaPlayerBridge_setDataSource(
+ env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(),
+ j_cookies.obj(), j_user_agent.obj(), hide_url_log_)) {
OnMediaError(MEDIA_ERROR_FORMAT);
+ return;
}
- return;
- }
-
- if (!Java_MediaPlayerBridge_setDataSource(
- env, j_media_player_bridge_.obj(), j_context, j_url_string.obj(),
- j_cookies.obj(), j_user_agent.obj(), hide_url_log_)) {
- OnMediaError(MEDIA_ERROR_FORMAT);
- return;
}
request_media_resources_cb_.Run(player_id());
OnMediaError(MEDIA_ERROR_FORMAT);
}
+bool MediaPlayerBridge::InterceptMediaUrl(
+ const std::string& url, int* fd, int64* offset, int64* size) {
+ // Sentinel value to check whether the output arguments have been set.
+ const int kUnsetValue = -1;
+
+ *fd = kUnsetValue;
+ *offset = kUnsetValue;
+ *size = kUnsetValue;
+ media::MediaUrlInterceptor* url_interceptor =
+ manager()->GetMediaUrlInterceptor();
+ if (url_interceptor && url_interceptor->Intercept(url, fd, offset, size)) {
+ DCHECK_NE(kUnsetValue, *fd);
+ DCHECK_NE(kUnsetValue, *offset);
+ DCHECK_NE(kUnsetValue, *size);
+ return true;
+ }
+ return false;
+}
+
void MediaPlayerBridge::OnDidSetDataUriDataSource(JNIEnv* env, jobject obj,
jboolean success) {
if (!success) {
}
void MediaPlayerBridge::ExtractMediaMetadata(const std::string& url) {
- manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
- url,
- cookies_,
- user_agent_,
- base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
- weak_factory_.GetWeakPtr()));
+ int fd;
+ int64 offset;
+ int64 size;
+ if (InterceptMediaUrl(url, &fd, &offset, &size)) {
+ manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
+ fd, offset, size,
+ base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
+ weak_factory_.GetWeakPtr()));
+ } else {
+ manager()->GetMediaResourceGetter()->ExtractMediaMetadata(
+ url, cookies_, user_agent_,
+ base::Bind(&MediaPlayerBridge::OnMediaMetadataExtracted,
+ weak_factory_.GetWeakPtr()));
+ }
}
void MediaPlayerBridge::OnMediaMetadataExtracted(
MediaPlayerManager* manager,
const RequestMediaResourcesCB& request_media_resources_cb,
const ReleaseMediaResourcesCB& release_media_resources_cb,
- const GURL& frame_url);
+ const GURL& frame_url,
+ bool allow_credentials);
virtual ~MediaPlayerBridge();
// Initialize this object and extract the metadata from the media.
void OnMediaMetadataExtracted(base::TimeDelta duration, int width, int height,
bool success);
+ // Returns true if a MediaUrlInterceptor registered by the embedder has
+ // intercepted the url.
+ bool InterceptMediaUrl(
+ const std::string& url, int* fd, int64* offset, int64* size);
+
// Whether the player is prepared for playback.
bool prepared_;
// Volume of playback.
double volume_;
+ // Whether user credentials are allowed to be passed.
+ bool allow_credentials_;
+
// Weak pointer passed to |listener_| for callbacks.
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<MediaPlayerBridge> weak_factory_;
class MediaPlayerAndroid;
class MediaResourceGetter;
+class MediaUrlInterceptor;
// This class is responsible for managing active MediaPlayerAndroid objects.
class MEDIA_EXPORT MediaPlayerManager {
public:
virtual ~MediaPlayerManager() {}
- // Return a pointer to the MediaResourceGetter object.
+ // Returns a pointer to the MediaResourceGetter object.
virtual MediaResourceGetter* GetMediaResourceGetter() = 0;
+ // Returns a pointer to the MediaUrlInterceptor object or null.
+ virtual MediaUrlInterceptor* GetMediaUrlInterceptor() = 0;
+
// Called when time update messages need to be sent. Args: player ID,
// current time.
virtual void OnTimeUpdate(int player_id, base::TimeDelta current_time) = 0;
const GURL& url,
const GetPlatformPathCB& callback) = 0;
- // Extract the metadata from a media URL. Once completed, the provided
+ // Extracts the metadata from a media URL. Once completed, the provided
// callback function will be run.
virtual void ExtractMediaMetadata(
const std::string& url,
const std::string& cookies,
const std::string& user_agent,
const ExtractMediaMetadataCB& callback) = 0;
+
+ // Extracts the metadata from a file descriptor. Once completed, the
+ // provided callback function will be run.
+ virtual void ExtractMediaMetadata(
+ const int fd,
+ const int64 offset,
+ const int64 size,
+ const ExtractMediaMetadataCB& callback) = 0;
};
} // namespace media
#include "media/base/android/media_drm_bridge.h"
#include "media/base/android/media_player_manager.h"
#include "media/base/android/media_source_player.h"
+#include "media/base/android/media_url_interceptor.h"
#include "media/base/android/video_decoder_job.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/decoder_buffer.h"
virtual MediaResourceGetter* GetMediaResourceGetter() OVERRIDE {
return NULL;
}
+ virtual MediaUrlInterceptor* GetMediaUrlInterceptor() OVERRIDE {
+ return NULL;
+ }
virtual void OnTimeUpdate(int player_id,
base::TimeDelta current_time) OVERRIDE {
timestamp_updated_ = true;
--- /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 MEDIA_BASE_ANDROID_MEDIA_URL_INTERCEPTOR_H_
+#define MEDIA_BASE_ANDROID_MEDIA_URL_INTERCEPTOR_H_
+
+#include <string>
+
+#include "base/android/jni_android.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Interceptor for content embedders to handle custom media urls
+// and translate them into files containing media.
+class MEDIA_EXPORT MediaUrlInterceptor {
+ public:
+ virtual ~MediaUrlInterceptor() {};
+
+ // Returns true if the embedder has intercepted the url and
+ // false otherwise.
+ // Output arguments (only when the url has been intercepted):
+ // - |fd|: file descriptor to the file containing the media element.
+ // - |offset|: offset in bytes from the start of the file to the
+ // media element.
+ // - |size|: size in bytes of the media element.
+ virtual bool Intercept(const std::string& url,
+ int* fd,
+ int64* offset,
+ int64* size) const = 0;
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_ANDROID_MEDIA_URL_INTERCEPTOR_H_
type_(UNKNOWN),
end_of_stream_(false),
last_packet_timestamp_(kNoTimestamp()),
- last_packet_duration_(kNoTimestamp()),
bitstream_converter_enabled_(false) {
DCHECK(demuxer_);
stream_->time_base, packet->pts));
buffer->set_duration(ConvertStreamTimestamp(
stream_->time_base, packet->duration));
-
- if (last_packet_timestamp_ != kNoTimestamp()) {
- // FFmpeg doesn't support chained ogg correctly. Instead of guaranteeing
- // continuity across links in the chain it uses the timestamp information
- // from each link directly. Doing so can lead to timestamps which appear to
- // go backwards in time.
- //
- // If the new link starts with a negative timestamp or a timestamp less than
- // the original (positive) |start_time|, we will get a negative timestamp
- // here. It's also possible FFmpeg returns kNoTimestamp() here if it's not
- // able to work out a timestamp using the previous link and the next.
- //
- // Fixing chained ogg is non-trivial, so for now just reuse the last good
- // timestamp. The decoder will rewrite the timestamps to be sample accurate
- // later. See http://crbug.com/396864.
- if (buffer->timestamp() == kNoTimestamp() ||
- buffer->timestamp() < last_packet_timestamp_) {
- buffer->set_timestamp(last_packet_timestamp_ +
- (last_packet_duration_ != kNoTimestamp()
- ? last_packet_duration_
- : base::TimeDelta::FromMicroseconds(1)));
- }
-
- // The demuxer should always output positive timestamps.
- DCHECK(buffer->timestamp() >= base::TimeDelta());
- DCHECK(buffer->timestamp() != kNoTimestamp());
-
- if (last_packet_timestamp_ < buffer->timestamp()) {
- buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp());
- demuxer_->NotifyBufferingChanged();
- }
+ if (buffer->timestamp() != kNoTimestamp() &&
+ last_packet_timestamp_ != kNoTimestamp() &&
+ last_packet_timestamp_ < buffer->timestamp()) {
+ buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp());
+ demuxer_->NotifyBufferingChanged();
}
-
last_packet_timestamp_ = buffer->timestamp();
- last_packet_duration_ = buffer->duration();
buffer_queue_.Push(buffer);
SatisfyPendingRead();
buffer_queue_.Clear();
end_of_stream_ = false;
last_packet_timestamp_ = kNoTimestamp();
- last_packet_duration_ = kNoTimestamp();
}
void FFmpegDemuxerStream::Stop() {
base::TimeDelta duration_;
bool end_of_stream_;
base::TimeDelta last_packet_timestamp_;
- base::TimeDelta last_packet_duration_;
Ranges<base::TimeDelta> buffered_ranges_;
DecoderBufferQueue buffer_queue_;
'base/android/media_player_listener.h',
'base/android/media_source_player.cc',
'base/android/media_source_player.h',
+ 'base/android/media_url_interceptor.h',
'base/android/video_decoder_job.cc',
'base/android/video_decoder_job.h',
'base/android/webaudio_media_codec_bridge.cc',
try {
final String GET_HOST_NAME = "getHost";
final String GET_PORT_NAME = "getPort";
- Object props = intent.getExtras().get("proxy");
- if (props == null) {
- return null;
- }
String className;
+ String proxyInfo;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
className = "android.net.ProxyProperties";
+ proxyInfo = "proxy";
} else {
className = "android.net.ProxyInfo";
+ proxyInfo = "android.intent.extra.PROXY_INFO";
+ }
+
+ Object props = intent.getExtras().get(proxyInfo);
+ if (props == null) {
+ return null;
}
Class<?> cls = Class.forName(className);
--- /dev/null
+
+PASS if this test didn't crash.
+
--- /dev/null
+<!DOCTYPE html>
+<link rel="import" href="resources/import-layout-with-pending-sheet.html">
+<p>PASS if this test didn't crash.</p>
--- /dev/null
+<!DOCTYPE html>
+<script>
+if (window.testRunner)
+ testRunner.dumpAsText();
+
+setTimeout(function(){
+ document.querySelector("link").import.querySelector("p").scrollLeft;
+}, 0);
+</script>
+<p></p>
+<link rel="stylesheet" href="style.css">
// We need to manually repaint because we avoid doing all repaints in layout or style
// recalc while sheets are still loading to avoid FOUC.
m_pendingSheetLayout = IgnoreLayoutWithPendingSheets;
- renderView()->repaintViewAndCompositedLayers();
+
+ ASSERT(renderView() || importsController());
+ if (renderView())
+ renderView()->repaintViewAndCompositedLayers();
}
}
# See http://crbug.com/288037.
wideViewportQuirkEnabled initial=false
+# Used by the android_webview to support a horizontal height auto-sizing
+# mode.
+forceZeroLayoutHeight initial=false, invalidate=ViewportDescription
+
# Touch based text selection and editing on desktop.
# crbug.com/304873 tracks removal once it's been enabled on all platforms.
touchEditingEnabled initial=false
return 0;
}
-// FIXME: SmartClipData is eventually returned via
-// SLookSmartClip.DataExtractionListener:
-// http://img-developer.samsung.com/onlinedocs/sms/com/samsung/android/sdk/look/...
-// however the original author of this change chose to use a string-serialization
-// format (presumably to make IPC easy?).
-// If we're going to use this as a Pickle format, we should at least have the
-// read/write code in one place!
-String SmartClipData::toString()
+IntRect SmartClipData::rect() const
{
- if (m_isEmpty)
- return emptyString();
-
- const UChar fieldSeparator = 0xFFFE;
- const UChar rowSeparator = 0xFFFF;
+ return m_rect;
+}
- StringBuilder result;
- result.append(String::number(m_rect.x()));
- result.append(fieldSeparator);
- result.append(String::number(m_rect.y()));
- result.append(fieldSeparator);
- result.append(String::number(m_rect.width()));
- result.append(fieldSeparator);
- result.append(String::number(m_rect.height()));
- result.append(fieldSeparator);
- result.append(m_string);
- result.append(rowSeparator);
- return result.toString();
+const String& SmartClipData::clipData() const
+{
+ return m_string;
}
SmartClip::SmartClip(PassRefPtr<LocalFrame> frame)
{
}
- String toString();
+ IntRect rect() const;
+ const String& clipData() const;
private:
bool m_isEmpty;
PassRefPtr<WebCore::LocalFrame> initializeAsChildFrame(WebCore::FrameHost*, WebCore::FrameOwner*, const AtomicString& name, const AtomicString& fallbackName);
+ // Returns a hit-tested VisiblePosition for the given point
+ WebCore::VisiblePosition visiblePositionForWindowPoint(const WebPoint&);
+
private:
friend class FrameLoaderClientImpl;
void loadJavaScriptURL(const WebCore::KURL&);
- // Returns a hit-tested VisiblePosition for the given point
- WebCore::VisiblePosition visiblePositionForWindowPoint(const WebPoint&);
-
WebPlugin* focusedPluginIfInputMethodSupported();
FrameLoaderClientImpl m_frameLoaderClientImpl;
m_gestureTapHighlightEnabled = enableHighlight;
}
+void WebSettingsImpl::setForceZeroLayoutHeight(bool enabled)
+{
+ m_settings->setForceZeroLayoutHeight(enabled);
+}
+
void WebSettingsImpl::setAllowCustomScrollbarInMainFrame(bool enabled)
{
m_settings->setAllowCustomScrollbarInMainFrame(enabled);
virtual void setFantasyFontFamily(const WebString&, UScriptCode = USCRIPT_COMMON) OVERRIDE;
virtual void setFixedFontFamily(const WebString&, UScriptCode = USCRIPT_COMMON) OVERRIDE;
virtual void setGestureTapHighlightEnabled(bool) OVERRIDE;
+ virtual void setForceZeroLayoutHeight(bool) OVERRIDE;
virtual void setHyperlinkAuditingEnabled(bool) OVERRIDE;
virtual void setIgnoreMainFrameOverflowHiddenQuirk(bool) OVERRIDE;
virtual void setImagesEnabled(bool) OVERRIDE;
#include "core/dom/Text.h"
#include "core/editing/Editor.h"
#include "core/editing/FrameSelection.h"
+#include "core/editing/HTMLInterchange.h"
#include "core/editing/InputMethodController.h"
#include "core/editing/TextIterator.h"
+#include "core/editing/markup.h"
#include "core/events/KeyboardEvent.h"
#include "core/events/WheelEvent.h"
#include "core/frame/EventHandlerRegistry.h"
}
}
+ if (page()->settings().forceZeroLayoutHeight())
+ layoutSize.height = 0;
+
view->setLayoutSize(layoutSize);
}
m_contextMenuAllowed = false;
}
+// FIXME: This should be removed when the chromium side patch lands
+// http://codereview.chromium.org/260623004
WebString WebViewImpl::getSmartClipData(WebRect rect)
{
+ return WebString();
+}
+
+void WebViewImpl::getSmartClipData(WebRect rect, WebString& clipText, WebRect& clipRect)
+{
LocalFrame* frame = toLocalFrame(focusedWebCoreFrame());
if (!frame)
- return WebString();
- return WebCore::SmartClip(frame).dataForRect(rect).toString();
+ return;
+ SmartClipData clipData = WebCore::SmartClip(frame).dataForRect(rect);
+ clipText = clipData.clipData();
+ clipRect = clipData.rect();
+}
+
+void WebViewImpl::extractSmartClipData(WebRect rect, WebString& clipText, WebString& clipHtml, WebRect& clipRect)
+{
+ LocalFrame* localFrame = toLocalFrame(focusedWebCoreFrame());
+ if (!localFrame)
+ return;
+ SmartClipData clipData = WebCore::SmartClip(localFrame).dataForRect(rect);
+ clipText = clipData.clipData();
+ clipRect = clipData.rect();
+
+ WebLocalFrameImpl* frame = mainFrameImpl();
+ if (!frame)
+ return;
+ WebPoint startPoint(rect.x, rect.y);
+ WebPoint endPoint(rect.x + rect.width, rect.y + rect.height);
+ VisiblePosition startVisiblePosition = frame->visiblePositionForWindowPoint(startPoint);
+ VisiblePosition endVisiblePosition = frame->visiblePositionForWindowPoint(endPoint);
+
+ Position startPosition = startVisiblePosition.deepEquivalent();
+ Position endPosition = endVisiblePosition.deepEquivalent();
+
+ RefPtr<Range> range = Range::create(*startPosition.document(), startPosition, endPosition);
+ if (!range)
+ return;
+
+ clipHtml = createMarkup(range.get(), 0, AnnotateForInterchange, false, ResolveNonLocalURLs);
}
void WebViewImpl::hidePopups()
unsigned inactiveForegroundColor) OVERRIDE;
virtual void performCustomContextMenuAction(unsigned action) OVERRIDE;
virtual void showContextMenu() OVERRIDE;
+ // FIXME: This should be removed when the chromium side patch lands
+ // http://codereview.chromium.org/260623004
virtual WebString getSmartClipData(WebRect) OVERRIDE;
+ virtual void getSmartClipData(WebRect, WebString&, WebRect&) OVERRIDE;
+ virtual void extractSmartClipData(WebRect, WebString&, WebString&, WebRect&) OVERRIDE;
virtual void hidePopups() OVERRIDE;
virtual void addPageOverlay(WebPageOverlay*, int /* zOrder */) OVERRIDE;
virtual void removePageOverlay(WebPageOverlay*) OVERRIDE;
EXPECT_EQ(0.5f, webViewHelper.webView()->pageScaleFactor());
}
+TEST_F(WebFrameTest, SetForceZeroLayoutHeight)
+{
+ UseMockScrollbarSettings mockScrollbarSettings;
+ registerMockedHttpURLLoad("200-by-300.html");
+
+ FixedLayoutTestWebViewClient client;
+ client.m_screenInfo.deviceScaleFactor = 1;
+ int viewportWidth = 640;
+ int viewportHeight = 480;
+
+ FrameTestHelpers::WebViewHelper webViewHelper;
+
+ webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html", true, 0, &client, enableViewportSettings);
+ webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
+ webViewHelper.webView()->layout();
+
+ EXPECT_LE(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
+ webViewHelper.webView()->settings()->setForceZeroLayoutHeight(true);
+ EXPECT_TRUE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout());
+
+ EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
+
+ webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight * 2));
+ EXPECT_FALSE(webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->needsLayout());
+ EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
+
+ webViewHelper.webView()->resize(WebSize(viewportWidth * 2, viewportHeight));
+ webViewHelper.webView()->layout();
+ EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
+
+ webViewHelper.webView()->settings()->setForceZeroLayoutHeight(false);
+ EXPECT_LE(viewportHeight, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
+}
+
+TEST_F(WebFrameTest, SetForceZeroLayoutHeightWorksAcrossNavigations)
+{
+ UseMockScrollbarSettings mockScrollbarSettings;
+ registerMockedHttpURLLoad("200-by-300.html");
+ registerMockedHttpURLLoad("large-div.html");
+
+ FixedLayoutTestWebViewClient client;
+ client.m_screenInfo.deviceScaleFactor = 1;
+ int viewportWidth = 640;
+ int viewportHeight = 480;
+
+ FrameTestHelpers::WebViewHelper webViewHelper;
+
+ webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html", true, 0, &client, enableViewportSettings);
+ webViewHelper.webView()->settings()->setForceZeroLayoutHeight(true);
+ webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
+ webViewHelper.webView()->layout();
+
+ FrameTestHelpers::loadFrame(webViewHelper.webView()->mainFrame(), m_baseURL + "large-div.html");
+ webViewHelper.webView()->layout();
+
+ EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
+}
+
+TEST_F(WebFrameTest, SetForceZeroLayoutHeightWithWideViewportQuirk)
+{
+ UseMockScrollbarSettings mockScrollbarSettings;
+ registerMockedHttpURLLoad("200-by-300.html");
+
+ FixedLayoutTestWebViewClient client;
+ client.m_screenInfo.deviceScaleFactor = 1;
+ int viewportWidth = 640;
+ int viewportHeight = 480;
+
+ FrameTestHelpers::WebViewHelper webViewHelper;
+
+ webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html", true, 0, &client, enableViewportSettings);
+ webViewHelper.webView()->settings()->setWideViewportQuirkEnabled(true);
+ webViewHelper.webView()->settings()->setUseWideViewport(true);
+ webViewHelper.webView()->settings()->setForceZeroLayoutHeight(true);
+ webViewHelper.webView()->resize(WebSize(viewportWidth, viewportHeight));
+ webViewHelper.webView()->layout();
+
+ EXPECT_EQ(0, webViewHelper.webViewImpl()->mainFrameImpl()->frameView()->layoutSize().height());
+}
+
TEST_F(WebFrameTest, WideViewportAndWideContentWithInitialScale)
{
UseMockScrollbarSettings mockScrollbarSettings;
TEST_F(WebViewTest, SmartClipData)
{
+ static const char* kExpectedClipText = "\nPrice 10,000,000won";
+ static const char* kExpectedClipHtml =
+ "<div id=\"div4\" style=\"padding: 10px; margin: 10px; border: 2px "
+ "solid rgb(135, 206, 235); float: left; width: 190px; height: 30px; "
+ "color: rgb(0, 0, 0); font-family: myahem; font-size: 8px; font-style: "
+ "normal; font-variant: normal; font-weight: normal; letter-spacing: "
+ "normal; line-height: normal; orphans: auto; text-align: start; "
+ "text-indent: 0px; text-transform: none; white-space: normal; widows: "
+ "auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;\">Air "
+ "conditioner</div><div id=\"div5\" style=\"padding: 10px; margin: "
+ "10px; border: 2px solid rgb(135, 206, 235); float: left; width: "
+ "190px; height: 30px; color: rgb(0, 0, 0); font-family: myahem; "
+ "font-size: 8px; font-style: normal; font-variant: normal; "
+ "font-weight: normal; letter-spacing: normal; line-height: normal; "
+ "orphans: auto; text-align: start; text-indent: 0px; text-transform: "
+ "none; white-space: normal; widows: auto; word-spacing: 0px; "
+ "-webkit-text-stroke-width: 0px;\">Price 10,000,000won</div>";
+ 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.html"));
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "smartclip.html");
webView->resize(WebSize(500, 500));
webView->layout();
WebRect cropRect(300, 125, 100, 50);
-
- // FIXME: We should test the structure of the data we get back.
- EXPECT_FALSE(webView->getSmartClipData(cropRect).isEmpty());
+ webView->extractSmartClipData(cropRect, clipText, clipHtml, clipRect);
+ EXPECT_STREQ(kExpectedClipText, clipText.utf8().c_str());
+ EXPECT_STREQ(kExpectedClipHtml, clipHtml.utf8().c_str());
}
class CreateChildCounterFrameClient : public FrameTestHelpers::TestWebFrameClient {
virtual void setFantasyFontFamily(const WebString&, UScriptCode = USCRIPT_COMMON) = 0;
virtual void setFixedFontFamily(const WebString&, UScriptCode = USCRIPT_COMMON) = 0;
virtual void setGestureTapHighlightEnabled(bool) = 0;
+ virtual void setForceZeroLayoutHeight(bool) = 0;
virtual void setHyperlinkAuditingEnabled(bool) = 0;
virtual void setIgnoreMainFrameOverflowHiddenQuirk(bool) = 0;
virtual void setImagesEnabled(bool) = 0;
// SmartClip support ---------------------------------------------------
+ // FIXME: This should be removed when the chromium side patch lands
+ // http://codereview.chromium.org/260623004
virtual WebString getSmartClipData(WebRect) = 0;
+ // TODO(changwan): remove this
+ virtual void getSmartClipData(WebRect, WebString&, WebRect& resultRect) = 0;
+ virtual void extractSmartClipData(WebRect initRect, WebString& text, WebString& html, WebRect& resultRect) = 0;
+
// Popup menu ----------------------------------------------------------
else
{
const TStructure *structure = type.getStruct();
- const TString &typeName = (structure ? structureTypeName(*structure, false, false) : typeString(type));
+ // If this is a nameless struct, we need to use its full definition, rather than its (empty) name.
+ // TypeString() will invoke defineNameless in this case, but layout qualifiers, if relevant, will not
+ // be taken into account.
+ const TString &typeName = ((structure && !structure->name().empty()) ?
+ structureTypeName(*structure, false, false) : typeString(type));
const TString ®isterString = TString("register(") + RegisterPrefix(type) + str(registerIndex) + ")";
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
-import android.database.Cursor;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;
import org.chromium.base.CalledByNative;
+import org.chromium.base.ContentUriUtils;
import org.chromium.base.JNINamespace;
import org.chromium.ui.R;
}
/**
- * @return the display name of the @code uri if present in the database
- * or an empty string otherwise.
- */
- private String resolveFileName(Uri uri, ContentResolver contentResolver) {
- if (contentResolver == null || uri == null) return "";
- Cursor cursor = null;
- try {
- cursor = contentResolver.query(uri, null, null, null, null);
-
- if (cursor != null && cursor.getCount() >= 1) {
- cursor.moveToFirst();
- int index = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
- if (index > -1) return cursor.getString(index);
- }
- } catch (NullPointerException e) {
- // Some android models don't handle the provider call correctly.
- // see crbug.com/345393
- return "";
- } finally {
- if (cursor != null) cursor.close();
- }
- return "";
- }
-
- /**
* Callback method to handle the intent results and pass on the path to the native
* SelectFileDialog.
* @param window The window that has access to the application activity.
}
if (ContentResolver.SCHEME_CONTENT.equals(results.getScheme())) {
- nativeOnFileSelected(mNativeSelectFileDialog,
- results.getData().toString(),
- resolveFileName(results.getData(),
- contentResolver));
+ GetDisplayNameTask task = new GetDisplayNameTask(contentResolver, false);
+ task.execute(results.getData());
return;
}
return false;
}
+ private class GetDisplayNameTask extends AsyncTask<Uri, Void, String[]> {
+ String[] mFilePaths;
+ final ContentResolver mContentResolver;
+ final boolean mIsMultiple;
+
+ public GetDisplayNameTask(ContentResolver contentResolver, boolean isMultiple) {
+ mContentResolver = contentResolver;
+ mIsMultiple = isMultiple;
+ }
+
+ @Override
+ protected String[] doInBackground(Uri...uris) {
+ mFilePaths = new String[uris.length];
+ String[] displayNames = new String[uris.length];
+ for (int i = 0; i < uris.length; i++) {
+ mFilePaths[i] = uris[i].toString();
+ displayNames[i] = ContentUriUtils.getDisplayName(
+ uris[i], mContentResolver, MediaStore.MediaColumns.DISPLAY_NAME);
+ }
+ return displayNames;
+ }
+
+ @Override
+ protected void onPostExecute(String[] result) {
+ if (!mIsMultiple) {
+ nativeOnFileSelected(mNativeSelectFileDialog, mFilePaths[0], result[0]);
+ }
+ }
+ }
+
@CalledByNative
private static SelectFileDialog create(long nativeSelectFileDialog) {
return new SelectFileDialog(nativeSelectFileDialog);
return details;
Window* target = GetGestureTarget(gestures->get().at(0));
+ if (!target)
+ return details;
+
for (size_t i = 0; i < gestures->size(); ++i) {
ui::GestureEvent* event = gestures->get().at(i);
event->ConvertLocationToTarget(window(), target);
EXPECT_TRUE(recorder.events().empty());
}
+// Verifies that a direct call to ProcessedTouchEvent() with a
+// TOUCH_PRESSED event does not cause a crash.
+TEST_F(WindowEventDispatcherTest, CallToProcessedTouchEvent) {
+ test::TestWindowDelegate delegate;
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ &delegate, 1, gfx::Rect(50, 50, 100, 100), root_window()));
+
+ ui::TouchEvent touch(
+ ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 1, ui::EventTimeForNow());
+ host()->dispatcher()->ProcessedTouchEvent(
+ &touch, window.get(), ui::ER_UNHANDLED);
+}
+
// This event handler requests the dispatcher to start holding pointer-move
// events when it receives the first scroll-update gesture.
class HoldPointerOnScrollHandler : public ui::test::TestEventHandler {
{VKEY_VOLUME_MUTE, "VolumeMute"},
{VKEY_VOLUME_DOWN, "VolumeDown"},
{VKEY_VOLUME_UP, "VolumeUp"},
+ {VKEY_BRIGHTNESS_DOWN, "BrightnessDown"},
+ {VKEY_BRIGHTNESS_UP, "BrightnessUp"},
+ {VKEY_MEDIA_LAUNCH_APP1, "ChromeOSSwitchWindow"},
+ {VKEY_MEDIA_LAUNCH_APP2, "ChromeOSFullscreen"},
{VKEY_MEDIA_NEXT_TRACK, "MediaTrackNext"},
{VKEY_MEDIA_PREV_TRACK, "MediaTrackPrevious"},
{VKEY_MEDIA_STOP, "MediaStop"},
class KeyCodeMap {
public:
KeyCodeMap() {
- for (size_t i = 0; i < arraysize(kKeyCodeTable); ++i)
- map_[kKeyCodeTable[i].dom_code] = kKeyCodeTable[i].keyboard_code;
+ for (size_t i = 0; i < arraysize(kKeyCodeTable); ++i) {
+ map_dom_key_[kKeyCodeTable[i].dom_code] = kKeyCodeTable[i].keyboard_code;
+ map_key_dom_[kKeyCodeTable[i].keyboard_code] = kKeyCodeTable[i].dom_code;
+ }
}
KeyboardCode GetKeyboardCode(const std::string& dom_code) const {
std::map<std::string, KeyboardCode>::const_iterator it =
- map_.find(dom_code);
- return (it == map_.end()) ? VKEY_UNKNOWN : it->second;
+ map_dom_key_.find(dom_code);
+ return (it == map_dom_key_.end()) ? VKEY_UNKNOWN : it->second;
+ }
+
+ std::string GetDomKeycode(KeyboardCode key_code) const {
+ std::map<KeyboardCode, std::string>::const_iterator it =
+ map_key_dom_.find(key_code);
+ return (it == map_key_dom_.end()) ? "" : it->second;
}
private:
- std::map<std::string, KeyboardCode> map_;
+ std::map<std::string, KeyboardCode> map_dom_key_;
+ std::map<KeyboardCode, std::string> map_key_dom_;
};
base::LazyInstance<KeyCodeMap>::Leaky g_keycode_map =
return g_keycode_map.Get().GetKeyboardCode(code);
}
+std::string KeyboardCodeToDomKeycode(KeyboardCode code) {
+ return g_keycode_map.Get().GetDomKeycode(code);
+}
+
} // namespace ui
// Translates the DOM4 key code string to ui::KeyboardCode.
UI_BASE_EXPORT KeyboardCode DomKeycodeToKeyboardCode(const std::string& code);
+// Translates the ui::KeyboardCode to DOM4 key code string.
+UI_BASE_EXPORT std::string KeyboardCodeToDomKeycode(KeyboardCode code);
+
} // namespace ui
#endif // UI_BASE_IME_CHROMEOS_IME_KEYMAP_H_
"test/events_test_utils.h",
"test/events_test_utils_x11.cc",
"test/events_test_utils_x11.h",
+ "test/mock_motion_event.cc",
+ "test/mock_motion_event.h",
"test/platform_event_waiter.cc",
"test/platform_event_waiter.h",
"test/test_event_handler.cc",
"gestures/velocity_calculator_unittest.cc",
"gesture_detection/bitset_32_unittest.cc",
"gesture_detection/gesture_provider_unittest.cc",
- "gesture_detection/mock_motion_event.h",
- "gesture_detection/mock_motion_event.cc",
"gesture_detection/velocity_tracker_unittest.cc",
"gesture_detection/touch_disposition_gesture_filter_unittest.cc",
"keycodes/dom4/keycode_converter_unittest.cc",
'test/events_test_utils.h',
'test/events_test_utils_x11.cc',
'test/events_test_utils_x11.h',
+ 'test/mock_motion_event.cc',
+ 'test/mock_motion_event.h',
'test/platform_event_waiter.cc',
'test/platform_event_waiter.h',
'test/test_event_handler.cc',
'gestures/velocity_calculator_unittest.cc',
'gesture_detection/bitset_32_unittest.cc',
'gesture_detection/gesture_provider_unittest.cc',
- 'gesture_detection/mock_motion_event.h',
- 'gesture_detection/mock_motion_event.cc',
'gesture_detection/velocity_tracker_unittest.cc',
'gesture_detection/touch_disposition_gesture_filter_unittest.cc',
'keycodes/dom4/keycode_converter_unittest.cc',
#include "ui/events/event_constants.h"
#include "ui/events/gesture_detection/gesture_event_data.h"
#include "ui/events/gesture_detection/gesture_provider.h"
-#include "ui/events/gesture_detection/mock_motion_event.h"
#include "ui/events/gesture_detection/motion_event.h"
+#include "ui/events/test/mock_motion_event.h"
#include "ui/gfx/geometry/point_f.h"
using base::TimeDelta;
using base::TimeTicks;
+using ui::test::MockMotionEvent;
namespace ui {
namespace {
ACTION_POINTER_UP,
};
+ enum ToolType {
+ TOOL_TYPE_UNKNOWN,
+ TOOL_TYPE_FINGER,
+ TOOL_TYPE_STYLUS,
+ TOOL_TYPE_MOUSE,
+ };
+
+ enum ButtonType {
+ BUTTON_PRIMARY = 1 << 0,
+ BUTTON_SECONDARY = 1 << 1,
+ BUTTON_TERTIARY = 1 << 2,
+ BUTTON_BACK = 1 << 3,
+ BUTTON_FORWARD = 1 << 4,
+ };
+
// The implementer promises that |GetPointerId()| will never exceed this.
enum { MAX_POINTER_ID = 31 };
size_t historical_index) const = 0;
virtual float GetHistoricalY(size_t pointer_index,
size_t historical_index) const = 0;
+ virtual ToolType GetToolType(size_t pointer_index) const = 0;
+ virtual int GetButtonState() const = 0;
virtual scoped_ptr<MotionEvent> Clone() const = 0;
virtual scoped_ptr<MotionEvent> Cancel() const = 0;
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/events/gesture_detection/mock_motion_event.h"
#include "ui/events/gesture_detection/touch_disposition_gesture_filter.h"
+#include "ui/events/test/mock_motion_event.h"
+
+using ui::test::MockMotionEvent;
namespace ui {
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/events/gesture_detection/mock_motion_event.h"
#include "ui/events/gesture_detection/velocity_tracker_state.h"
+#include "ui/events/test/mock_motion_event.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/vector2d_f.h"
using base::TimeDelta;
using base::TimeTicks;
+using ui::test::MockMotionEvent;
namespace ui {
namespace {
return 0;
}
+MotionEvent::ToolType MotionEventAura::GetToolType(size_t pointer_index) const {
+ NOTIMPLEMENTED();
+ return MotionEvent::TOOL_TYPE_UNKNOWN;
+}
+
+int MotionEventAura::GetButtonState() const {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
scoped_ptr<MotionEvent> MotionEventAura::Clone() const {
return scoped_ptr<MotionEvent>(new MotionEventAura(pointer_count_,
last_touch_time_,
size_t historical_index) const OVERRIDE;
virtual float GetHistoricalY(size_t pointer_index,
size_t historical_index) const OVERRIDE;
+ virtual ToolType GetToolType(size_t pointer_index) const OVERRIDE;
+ virtual int GetButtonState() const OVERRIDE;
virtual scoped_ptr<MotionEvent> Clone() const OVERRIDE;
virtual scoped_ptr<MotionEvent> Cancel() const OVERRIDE;
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/events/gesture_detection/mock_motion_event.h"
+#include "ui/events/test/mock_motion_event.h"
#include "base/logging.h"
using base::TimeTicks;
namespace ui {
+namespace test {
MockMotionEvent::MockMotionEvent()
- : action(ACTION_CANCEL), pointer_count(1), touch_major(TOUCH_MAJOR), id(0) {
+ : action(ACTION_CANCEL), pointer_count(1), touch_major(TOUCH_MAJOR), id(0),
+ button_state(0) {
}
MockMotionEvent::MockMotionEvent(Action action)
- : action(action), pointer_count(1), touch_major(TOUCH_MAJOR), id(0) {
+ : action(action), pointer_count(1), touch_major(TOUCH_MAJOR), id(0),
+ button_state(0) {
}
MockMotionEvent::MockMotionEvent(Action action,
pointer_count(1),
time(time),
touch_major(TOUCH_MAJOR),
- id(0) {
+ id(0),
+ button_state(0) {
points[0].SetPoint(x, y);
+ tool_types[0] = TOOL_TYPE_UNKNOWN;
}
MockMotionEvent::MockMotionEvent(Action action,
pointer_count(2),
time(time),
touch_major(TOUCH_MAJOR),
- id(0) {
+ id(0),
+ button_state(0) {
points[0].SetPoint(x0, y0);
+ tool_types[0] = TOOL_TYPE_UNKNOWN;
points[1].SetPoint(x1, y1);
+ tool_types[1] = TOOL_TYPE_UNKNOWN;
}
MockMotionEvent::MockMotionEvent(Action action,
pointer_count(3),
time(time),
touch_major(TOUCH_MAJOR),
- id(0) {
+ id(0),
+ button_state(0) {
points[0].SetPoint(x0, y0);
+ tool_types[0] = TOOL_TYPE_UNKNOWN;
points[1].SetPoint(x1, y1);
+ tool_types[1] = TOOL_TYPE_UNKNOWN;
points[2].SetPoint(x2, y2);
+ tool_types[2] = TOOL_TYPE_UNKNOWN;
}
MockMotionEvent::MockMotionEvent(const MockMotionEvent& other)
pointer_count(other.pointer_count),
time(other.time),
touch_major(other.touch_major),
- id(other.GetId()) {
- for (size_t i = 0; i < pointer_count; ++i)
+ id(other.GetId()),
+ button_state(other.GetButtonState()) {
+ for (size_t i = 0; i < pointer_count; ++i) {
points[i] = other.points[i];
+ tool_types[i] = other.tool_types[i];
+ }
}
MockMotionEvent::~MockMotionEvent() {}
return 0;
}
+MotionEvent::ToolType MockMotionEvent::GetToolType(size_t pointer_index) const {
+ DCHECK_LT(pointer_index, pointer_count);
+ return tool_types[pointer_index];
+}
+
+int MockMotionEvent::GetButtonState() const {
+ return button_state;
+}
+
scoped_ptr<MotionEvent> MockMotionEvent::Clone() const {
return scoped_ptr<MotionEvent>(new MockMotionEvent(*this));
}
DCHECK_LT(pointer_count, static_cast<size_t>(MAX_POINTERS));
points[pointer_count++] = gfx::PointF(x, y);
+ tool_types[pointer_count] = TOOL_TYPE_UNKNOWN;
action = pointer_count > 1 ? ACTION_POINTER_DOWN : ACTION_DOWN;
}
void MockMotionEvent::MovePoint(size_t index, float x, float y) {
DCHECK_LT(index, pointer_count);
points[index] = gfx::PointF(x, y);
+ tool_types[index] = TOOL_TYPE_UNKNOWN;
action = ACTION_MOVE;
}
raw_offset.set_y(raw_offset_y);
}
+void MockMotionEvent::SetToolType(size_t pointer_index, ToolType tool_type) {
+ DCHECK_LT(pointer_index, pointer_count);
+ tool_types[pointer_index] = tool_type;
+}
+
+void MockMotionEvent::SetButtonState(int new_button_state) {
+ button_state = new_button_state;
+}
+
+} // namespace test
} // namespace ui
#include "ui/gfx/geometry/point_f.h"
namespace ui {
+namespace test {
struct MockMotionEvent : public MotionEvent {
enum { MAX_POINTERS = 3 };
base::TimeTicks time,
const std::vector<gfx::PointF>& positions);
MockMotionEvent(const MockMotionEvent& other);
+
virtual ~MockMotionEvent();
// MotionEvent methods.
size_t historical_index) const OVERRIDE;
virtual float GetHistoricalY(size_t pointer_index,
size_t historical_index) const OVERRIDE;
+ virtual ToolType GetToolType(size_t pointer_index) const OVERRIDE;
+ virtual int GetButtonState() const OVERRIDE;
virtual scoped_ptr<MotionEvent> Clone() const OVERRIDE;
virtual scoped_ptr<MotionEvent> Cancel() const OVERRIDE;
void CancelPoint();
void SetTouchMajor(float new_touch_major);
void SetRawOffset(float raw_offset_x, float raw_offset_y);
+ void SetToolType(size_t index, ToolType tool_type);
+ void SetButtonState(int button_state);
MotionEvent::Action action;
size_t pointer_count;
gfx::PointF points[MAX_POINTERS];
+ ToolType tool_types[MAX_POINTERS];
gfx::Vector2dF raw_offset;
base::TimeTicks time;
float touch_major;
int id;
+ int button_state;
};
+} // namespace test
} // namespace ui
static void DiscoverGreyObjectsInSpace(Heap* heap,
MarkingDeque* marking_deque,
PagedSpace* space) {
- if (!space->was_swept_conservatively()) {
- HeapObjectIterator it(space);
- DiscoverGreyObjectsWithIterator(heap, marking_deque, &it);
- } else {
- PageIterator it(space);
- while (it.has_next()) {
- Page* p = it.next();
- DiscoverGreyObjectsOnPage(marking_deque, p);
- if (marking_deque->IsFull()) return;
- }
+ PageIterator it(space);
+ while (it.has_next()) {
+ Page* p = it.next();
+ DiscoverGreyObjectsOnPage(marking_deque, p);
+ if (marking_deque->IsFull()) return;
}
}
ASSERT(target->NumberOfOwnDescriptors() ==
object->map()->NumberOfOwnDescriptors());
// This works since descriptors are sorted in order of addition.
- ASSERT(object->map()->instance_descriptors()->
- GetKey(descriptor_number) == *name);
+ ASSERT(Name::Equals(handle(object->map()->instance_descriptors()->
+ GetKey(descriptor_number)), name));
return TryAccessorTransition(object, target, descriptor_number,
component, accessor, attributes);
}
#define MAJOR_VERSION 3
#define MINOR_VERSION 27
#define BUILD_NUMBER 34
-#define PATCH_LEVEL 12
+#define PATCH_LEVEL 14
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
#define IS_CANDIDATE_VERSION 0
use_legacy_background_size_shorthand_behavior(false),
wide_viewport_quirk(false),
use_wide_viewport(true),
+ force_zero_layout_height(false),
viewport_meta_layout_size_quirk(false),
viewport_meta_merge_content_quirk(false),
viewport_meta_non_user_scalable_quirk(false),
bool use_legacy_background_size_shorthand_behavior;
bool wide_viewport_quirk;
bool use_wide_viewport;
+ bool force_zero_layout_height;
bool viewport_meta_layout_size_quirk;
bool viewport_meta_merge_content_quirk;
bool viewport_meta_non_user_scalable_quirk;
<translation id="1930711995431081526">статус</translation>
<translation id="658823671542763450">пређите на режим целог екрана</translation>
<translation id="7720026100085573005">преостало време</translation>
-<translation id="370665806235115550">Учитавање...</translation>
+<translation id="370665806235115550">Учитава се...</translation>
<translation id="2723001399770238859">аудио</translation>
<translation id="6845533974506654842">притисни</translation>
<translation id="8244226242650769279">мапа слике</translation>
# Edit these when rolling DEPS.xwalk.
# -----------------------------------
-chromium_crosswalk_rev = 'f506465773c4515957798e38250a3a7becb2bea6'
-blink_crosswalk_rev = 'ebedaf16c0976221debb8547c9a5fc0017998090'
-v8_crosswalk_rev = '8ddfc6f1a103ddbe20e38afeb12c3e0666c8a361'
+chromium_crosswalk_rev = 'b959b9627080362e119a6e4425c9c2b0e4f313b9'
+blink_crosswalk_rev = '8971a658e086b47f630db86850014fef0d065f8f'
+v8_crosswalk_rev = '33bed61998cd976161d9c239832e81699785191e'
ozone_wayland_rev = '3372a0e23d925d5402eb16abbbe58dd82b583a5a'
crosswalk_git = 'https://github.com/crosswalk-project'
MAJOR=9
MINOR=37
-BUILD=195
+BUILD=197
PATCH=0
runtime_context_(runtime_context),
observer_(observer),
entry_point_used_(Default),
+ remote_debugging_enabled_(false),
weak_factory_(this) {
DCHECK(runtime_context_);
DCHECK(data_.get());
if (!url.is_valid())
return false;
+ remote_debugging_enabled_ = launch_params.remote_debugging;
+
Runtime* runtime = Runtime::Create(
runtime_context_,
this, content::SiteInstance::CreateForURL(runtime_context_, url));
void Application::OnRuntimeAdded(Runtime* runtime) {
DCHECK(runtime);
+ runtime->set_remote_debugging_enabled(remote_debugging_enabled_);
runtimes_.insert(runtime);
}
LaunchParams() :
entry_points(Default),
launcher_pid(0),
- force_fullscreen(false) {}
+ force_fullscreen(false),
+ remote_debugging(false) {}
LaunchEntryPoints entry_points;
int32 launcher_pid;
bool force_fullscreen;
+ bool remote_debugging;
};
// Closes all the application's runtimes (application pages).
StoredPermissionMap permission_map_;
// Security policy.
scoped_ptr<SecurityPolicy> security_policy_;
+ // Remote debugging enabled or not for this Application
+ bool remote_debugging_enabled_;
// WeakPtrFactory should be always declared the last.
base::WeakPtrFactory<Application> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Application);
#include "base/command_line.h"
#include "base/file_util.h"
#include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_switches.h"
#include "net/base/filename_util.h"
#include "xwalk/application/browser/application.h"
#include "xwalk/application/browser/application_service.h"
const T& param, const base::CommandLine& cmd_line) {
Application::LaunchParams launch_params;
launch_params.force_fullscreen = cmd_line.HasSwitch(switches::kFullscreen);
+ launch_params.remote_debugging =
+ cmd_line.HasSwitch(switches::kRemoteDebuggingPort);
return application_service_->Launch(param, launch_params);
}
Application::LaunchParams launch_params;
launch_params.force_fullscreen = cmd_line.HasSwitch(switches::kFullscreen);
launch_params.entry_points = Application::StartURLKey;
+ launch_params.remote_debugging =
+ cmd_line.HasSwitch(switches::kRemoteDebuggingPort);
return !!application_service_->Launch(application_data, launch_params);
}
// We might want to pass key-value pairs if have more parameters in future.
unsigned int launcher_pid;
bool fullscreen;
+ bool remote_debugging;
if (!reader.PopString(&app_id_or_url) ||
!reader.PopUint32(&launcher_pid) ||
- !reader.PopBool(&fullscreen)) {
+ !reader.PopBool(&fullscreen) ||
+ !reader.PopBool(&remote_debugging)) {
scoped_ptr<dbus::Response> response =
CreateError(method_call,
"Error parsing message. Missing arguments.");
Application::LaunchParams params;
params.launcher_pid = launcher_pid;
params.force_fullscreen = fullscreen;
+ params.remote_debugging = remote_debugging;
Application* application;
if (GURL(app_id_or_url).spec().empty()) {
scoped_ptr<Package> package;
if (!base::DirectoryExists(path)) {
package = Package::Create(path);
+ if (!package->IsValid())
+ return false;
package->Extract(&unpacked_dir);
app_id = package->Id();
} else {
#include "base/files/file_path.h"
#include "libxml/tree.h"
#include "libxml/xpath.h"
-#include "xwalk/application/common/signature_types.h"
namespace xwalk {
namespace application {
certificate_list_ = certificate_list;
}
- ReferenceHashMap& reference_hash_map() {
- return reference_hash_map_;
- }
-
- void set_reference_hash_map(const ReferenceHashMap& reference_hash_map) {
- reference_hash_map_ = reference_hash_map;
- }
-
bool isAuthorSignature() const {
return signature_number_ == -1;
}
std::string signature_method_;
std::set<std::string> reference_set_;
std::list<std::string> certificate_list_;
- ReferenceHashMap reference_hash_map_;
DISALLOW_COPY_AND_ASSIGN(SignatureData);
};
return false;
}
- std::string uri, transform_algorithm, digest_method, digest_value;
- xmlNodePtr refer_node, transforms_node, transform_node, digest_method_node,
- digest_value_node;
- ReferenceData reference_data;
+ std::string uri;
+ xmlNodePtr refer_node;
std::set<std::string> reference_set;
- ReferenceHashMap reference_hash_map;
for (size_t i = 0; i < reference_vec.size(); ++i) {
refer_node = reference_vec[i];
uri = GetAttribute(refer_node, kTokenURI);
return false;
}
reference_set.insert(uri);
-
- // Parse <Transforms>
- transforms_node =
- GetFirstChild(refer_node, signature_ns, kTokenTransforms);
- if (!transforms_node) {
- transform_node =
- GetFirstChild(transforms_node, signature_ns, kTokenTransform);
- if (!transforms_node) {
- reference_data.transform_algorithm =
- GetAttribute(transform_node, kTokenAlgorithm);
- }
- }
-
- // Parse <DigestMethod>
- digest_method_node =
- GetFirstChild(refer_node, signature_ns, kTokenDigestMethod);
- if (!digest_method_node) {
- LOG(ERROR) << "Missing DigestMethod tag.";
- return false;
- }
- reference_data.digest_method =
- GetAttribute(digest_method_node, kTokenAlgorithm);
- if (reference_data.digest_method.empty()) {
- LOG(ERROR) << "Missing DigestMethod attribute.";
- return false;
- }
-
- // Parser <DigestValue>
- digest_value_node =
- GetFirstChild(refer_node, signature_ns, kTokenDigestValue);
- if (!digest_value_node) {
- LOG(ERROR) << "Missing DigestValue tag.";
- return false;
- }
- reference_data.digest_value =
- XmlStringToStdString(xmlNodeGetContent(digest_value_node));
- if (reference_data.digest_value.empty()) {
- LOG(ERROR) << "Missing DigestValue.";
- return false;
- }
- reference_hash_map.insert(make_pair(uri, reference_data));
}
data->set_reference_set(reference_set);
- data->set_reference_hash_map(reference_hash_map);
return true;
}
#include "xwalk/application/common/installer/tizen/signature_validator.h"
+#include <set>
#include <string>
#include "base/files/file_enumerator.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "libxml/tree.h"
#include "libxml/parser.h"
#include "libxml/xmlschemas.h"
#include "third_party/re2/re2/re2.h"
+#include "xwalk/application/common/installer/signature_data.h"
#include "xwalk/application/common/installer/signature_parser.h"
namespace {
"http://www.w3.org/ns/widgets-digsig#role-distributor";
const char kTokenProfileURI[] =
"http://www.w3.org/ns/widgets-digsig#profile";
-const char kSignatureSchemaPath[] = "/usr/share/xwalk/schema.xsd";
+const char kSignatureSchemaPath[] = "/usr/share/xwalk/signature_schema.xsd";
// A wrapper of LOG(ERROR) function, which is used as parameter of function
// xmlSchemaSetValidErrors
};
typedef std::set<SignatureFile> SignatureFileSet;
-const SignatureFileSet& GetSignatureFiles(const base::FilePath& widget_path) {
+const SignatureFileSet GetSignatureFiles(const base::FilePath& widget_path) {
SignatureFileSet signature_set;
std::string file_name;
int number;
bool CheckReference(
const xwalk::application::SignatureData& signature_data) {
base::FilePath widget_path = signature_data.GetExtractedWidgetPath();
- int prefix_length = widget_path.value().length() + 1;
+ int prefix_length = widget_path.value().length();
std::string file_name;
std::set<std::string> reference_set = signature_data.reference_set();
base::FileEnumerator iter(widget_path, true, base::FileEnumerator::FILES);
LOG(INFO) << "Verifying widget signature file.";
// Process every signature files (author and distributor) according to
// http://www.w3.org/TR/widgets-digsig/#signature-verification.
- SignatureFileSet signature_set = GetSignatureFiles(widget_path);
+ const SignatureFileSet& signature_set = GetSignatureFiles(widget_path);
if (signature_set.empty()) {
LOG(INFO) << "No signed signature in the package.";
return UNTRUSTED;
#ifndef XWALK_APPLICATION_COMMON_INSTALLER_TIZEN_SIGNATURE_VALIDATOR_H_
#define XWALK_APPLICATION_COMMON_INSTALLER_TIZEN_SIGNATURE_VALIDATOR_H_
-#include <set>
-#include <string>
-
#include "base/files/file_path.h"
-#include "base/memory/scoped_ptr.h"
-#include "xwalk/application/common/installer/signature_data.h"
-#include "xwalk/application/common/signature_types.h"
namespace xwalk {
namespace application {
+++ /dev/null
-// Copyright (c) 2013 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_SIGNATURE_TYPES_H_
-#define XWALK_APPLICATION_COMMON_SIGNATURE_TYPES_H_
-
-#include <map>
-#include <string>
-
-namespace xwalk {
-namespace application {
-
-struct ReferenceData {
- std::string transform_algorithm;
- std::string digest_method;
- std::string digest_value;
-};
-
-typedef std::map<std::string, ReferenceData> ReferenceHashMap;
-} // namespace application
-} // namespace xwalk
-
-#endif // XWALK_APPLICATION_COMMON_SIGNATURE_TYPES_H_
static char** g_argv;
static gboolean query_running = FALSE;
static gboolean fullscreen = FALSE;
+static gboolean remote_debugging = FALSE;
static gchar** cmd_appid_or_url;
static GOptionEntry entries[] = {
"Check whether the application is running", NULL },
{ "fullscreen", 'f', 0, G_OPTION_ARG_NONE, &fullscreen,
"Run the application as fullscreen", NULL },
+ { "debugging_port", 'd', 0, G_OPTION_ARG_NONE, &remote_debugging,
+ "Enable remote debugging for the application", NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &cmd_appid_or_url,
"ID of the application to be launched or URL to open", NULL },
{ NULL }
}
static void launch_application(const char* appid_or_url,
- gboolean fullscreen) {
+ gboolean fullscreen,
+ gboolean remote_debugging) {
ep_launcher = new XWalkExtensionProcessLauncher();
GError* error = NULL;
g_signal_connect(g_running_apps_manager, "object-removed",
unsigned int launcher_pid = getpid();
GVariant* result = g_dbus_proxy_call_sync(running_proxy, "Launch",
- g_variant_new("(sub)", appid_or_url, launcher_pid, fullscreen),
+ g_variant_new("(subb)", appid_or_url, launcher_pid, fullscreen,
+ remote_debugging),
G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
if (!result) {
fprintf(stderr, "Couldn't call 'Launch' method: %s\n", error->message);
return query_application_running(appid_or_url);
}
- launch_application(appid_or_url, fullscreen);
+ launch_application(appid_or_url, fullscreen, remote_debugging);
free(appid_or_url);
return 0;
}
} // namespace
-static void TerminateIfRunning(const std::string& app_id) {
- dbus::Bus::Options options;
-#if defined(OS_TIZEN_MOBILE)
- options.bus_type = dbus::Bus::CUSTOM_ADDRESS;
- options.address.assign("unix:path=/run/user/app/dbus/user_bus_socket");
-#endif
- scoped_refptr<dbus::Bus> bus(new dbus::Bus(options));
- dbus::ObjectProxy* app_proxy =
- bus->GetObjectProxy(xwalk_service_name, kRunningManagerDBusPath);
- if (!app_proxy)
- return;
-
- dbus::MethodCall method_call(
- xwalk_running_manager_iface, "TerminateIfRunning");
- dbus::MessageWriter writer(&method_call);
- writer.AppendString(app_id);
-
- app_proxy->CallMethodAndBlock(&method_call, 1000);
-}
-
static bool enable_remote_debugging(gint debugging_port) {
dbus::Bus::Options options;
#if defined(OS_TIZEN_MOBILE)
success = installer->Update(app_id, path);
}
} else if (uninstall_appid) {
-#if defined(SHARED_PROCESS_MODE)
- TerminateIfRunning(uninstall_appid);
-#endif
success = installer->Uninstall(uninstall_appid);
} else if (debugging_port >= 0) {
#if defined(SHARED_PROCESS_MODE)
%endif
Name: crosswalk
-Version: 9.37.195.0
+Version: 9.37.197.0
Release: 0
Summary: Crosswalk is an app runtime based on Chromium
License: (BSD-3-Clause and LGPL-2.1+)
GYP_EXTRA_FLAGS="${GYP_EXTRA_FLAGS} -Ddisable_fatal_linker_warnings=1"
%endif
-# Temporarily disable Alsa support while snd_seq_* support is not enabled on
-# Tizen. See https://codereview.chromium.org/264973012 and
-# https://review.tizen.org/gerrit/#/c/24336/
-GYP_EXTRA_FLAGS="${GYP_EXTRA_FLAGS} -Duse_alsa=0"
-
# Temporarily disable WebRTC support because its build currently hardcodes
# dependencies on X11 and OpenSSL. We are still trying to get some
# clarifications as to whether this is really necessary. See XWALK-2160.
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Rect;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
@CalledByNative
public void onGetFullscreenFlagFromManifest(boolean enterFullscreen) {
- if (enterFullscreen) mContentsClientBridge.onToggleFullscreen(true);
+ if (enterFullscreen) {
+ if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
+ View decorView = mXWalkView.getActivity().getWindow().getDecorView();
+ decorView.setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+ } else {
+ mXWalkView.getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
+ }
}
public void destroy() {
*
* @hide
*/
+ @XWalkAPI
public void setNetworkAvailable(boolean networkUp) {
if (mContent == null) return;
checkThreadSafety();
return -1;
}
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
+#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)
void XWalkMainDelegate::ZygoteStarting(
ScopedVector<content::ZygoteForkDelegate>* delegates) {
#if !defined(DISABLE_NACL)
virtual void PreSandboxStartup() OVERRIDE;
virtual int RunProcess(const std::string& process_type,
const content::MainFunctionParams& main_function_params) OVERRIDE;
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
+#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)
virtual void ZygoteStarting(
ScopedVector<content::ZygoteForkDelegate>* delegates) OVERRIDE;
#endif
for (std::vector<RenderViewHost*>::iterator it = rvh_list.begin();
it != rvh_list.end(); ++it) {
WebContents* web_contents = WebContents::FromRenderViewHost(*it);
- if (web_contents)
- targets.push_back(new Target(web_contents));
+ if (web_contents) {
+ Runtime* runtime = static_cast<Runtime*>(web_contents->GetDelegate());
+ if (runtime && runtime->remote_debugging_enabled()) {
+ targets.push_back(new Target(web_contents));
+ }
+ }
}
callback.Run(targets);
}
window_(NULL),
weak_ptr_factory_(this),
fullscreen_options_(NO_FULLSCREEN),
+ remote_debugging_enabled_(false),
observer_(observer) {
web_contents_->SetDelegate(this);
content::NotificationService::current()->Notify(
content::RenderProcessHost* GetRenderProcessHost();
+ void set_remote_debugging_enabled(bool enable) {
+ remote_debugging_enabled_ = enable;
+ }
+ bool remote_debugging_enabled() const { return remote_debugging_enabled_; }
+
#if defined(OS_TIZEN_MOBILE)
void CloseRootWindow();
#endif
};
unsigned int fullscreen_options_;
+ bool remote_debugging_enabled_;
Observer* observer_;
};
#define XWALK_RUNTIME_BROWSER_UI_NATIVE_APP_WINDOW_TIZEN_H_
#include "base/memory/scoped_ptr.h"
-#include "content/browser/screen_orientation/screen_orientation_provider.h"
+#include "third_party/WebKit/public/platform/WebScreenOrientationLockType.h"
+#include "ui/aura/window_observer.h"
#include "xwalk/runtime/browser/ui/screen_orientation.h"
#include "xwalk/runtime/browser/ui/native_app_window_views.h"
#include "xwalk/tizen/mobile/sensor/sensor_provider.h"
#include "xwalk/tizen/mobile/ui/tizen_system_indicator_widget.h"
#include "xwalk/tizen/mobile/ui/widget_container_view.h"
-#include "ui/aura/window_observer.h"
namespace xwalk {
: has_write_pending_(false),
is_suspended_(false),
is_reading_(false),
- resolver_(net::HostResolver::CreateDefaultResolver(NULL)),
read_buffer_(new net::IOBuffer(kBufferSize)),
write_buffer_(new net::IOBuffer(kBufferSize)),
+ resolver_(net::HostResolver::CreateDefaultResolver(NULL)),
single_resolver_(new net::SingleRequestHostResolver(resolver_.get())) {
handler_.Register("init",
base::Bind(&UDPSocketObject::OnInit, base::Unretained(this)));
--- /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.
+
+package org.xwalk.core.xwview.test;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.chromium.base.test.util.Feature;
+
+
+/**
+ * Tests for the hasEnteredFullscreen() and leaveFullscreen() method.
+ */
+public class EnterAndLeaveFullscreenTest extends XWalkViewTestBase {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @SmallTest
+ @Feature({"XWalkView", "Fullscreen"})
+ public void testEnterAndExitFullscreen() throws Throwable {
+ final String name = "fullscreen_enter_exit.html";
+ String fileContent = getFileContent(name);
+
+ loadDataSync(name, fileContent, "text/html", false);
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ clickOnElementId("enter_fullscreen");
+ assertTrue(getXWalkView().hasEnteredFullscreen());
+ getXWalkView().leaveFullscreen();
+ assertFalse(getXWalkView().hasEnteredFullscreen());
+
+ clickOnElementId("enter_fullscreen");
+ clickOnElementId("exit_fullscreen");
+ assertFalse(getXWalkView().hasEnteredFullscreen());
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+}
--- /dev/null
+<html>
+<head>
+<script>
+ function launchFullscreen(element) {
+ element.webkitRequestFullscreen();
+ }
+
+ function exitFullscreen() {
+ document.webkitExitFullscreen();
+ }
+</script>
+</head>
+<body>
+ <div>
+ <button id="enter_fullscreen" onclick="launchFullscreen(document.documentElement);">Launch Fullscreen</button>
+ <button id="exit_fullscreen" onclick="exitFullscreen();">Exit Fullscreen</button>
+ </div>
+</body>
+</html>
'<(PRODUCT_DIR)/xwalk_internal_xwview_test/assets/echo.html',
'<(PRODUCT_DIR)/xwalk_internal_xwview_test/assets/echoSync.html',
'<(PRODUCT_DIR)/xwalk_internal_xwview_test/assets/framesEcho.html',
+ '<(PRODUCT_DIR)/xwalk_xwview_test/assets/fullscreen_enter_exit.html',
'<(PRODUCT_DIR)/xwalk_xwview_test/assets/index.html',
'<(PRODUCT_DIR)/xwalk_xwview_test/assets/scale_changed.html',
'<(PRODUCT_DIR)/xwalk_xwview_test/assets/window.close.html',
'test/android/data/echo.html',
'test/android/data/echoSync.html',
'test/android/data/framesEcho.html',
+ 'test/android/data/fullscreen_enter_exit.html',
'test/android/data/index.html',
'test/android/data/scale_changed.html',
'test/android/data/window.close.html',