diff --git a/DEPS b/DEPS index 1c50f2c..a8c9bffa 100644 --- a/DEPS +++ b/DEPS
@@ -44,7 +44,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'cb99d142e27d0570b871503bbae9a5f1b19a594a', + 'v8_revision': 'b481bccc84783445e123b8b4e3ff9ecfdac2bd90', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other.
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn index cc114f8f..023bc64c 100644 --- a/android_webview/BUILD.gn +++ b/android_webview/BUILD.gn
@@ -583,6 +583,7 @@ "java/src/org/chromium/android_webview/AwPrintDocumentAdapter.java", "java/src/org/chromium/android_webview/AwSafeBrowsingConfigHelper.java", "java/src/org/chromium/android_webview/AwQuotaManagerBridge.java", + "java/src/org/chromium/android_webview/AwRendererPriorityManager.java", "java/src/org/chromium/android_webview/AwRenderProcessGoneDetail.java", "java/src/org/chromium/android_webview/AwResource.java", "java/src/org/chromium/android_webview/AwScrollOffsetManager.java", @@ -646,7 +647,10 @@ deps += [ "//components/spellcheck/browser/android:java" ] } - srcjar_deps = [ "//android_webview/native:aw_permission_request_resource" ] + srcjar_deps = [ + "//android_webview/native:aw_permission_request_resource", + "//android_webview/native:aw_renderer_priority_manager_renderer_priority", + ] } java_strings_grd("strings_grd") {
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java index 21f159b..0ea0849 100644 --- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java +++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromium.java
@@ -93,7 +93,7 @@ // Variables for functionality provided by this adapter --------------------------------------- private ContentSettingsAdapter mWebSettings; // The WebView wrapper for ContentViewCore and required browser compontents. - private AwContents mAwContents; + AwContents mAwContents; // Non-null if this webview is using the GL accelerated draw path. private DrawGLFunctor mGLfunctor;
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java index 3e1ed55..5e9fd1b 100644 --- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java +++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -179,7 +179,7 @@ private SharedPreferences mWebViewPrefs; private WebViewDelegate mWebViewDelegate; - private boolean mShouldDisableThreadChecking; + boolean mShouldDisableThreadChecking; /** * Entry point for newer versions of Android.
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java index 69393ce..6ccba0af 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -45,6 +45,7 @@ import org.chromium.android_webview.permission.AwGeolocationCallback; import org.chromium.android_webview.permission.AwPermissionRequest; +import org.chromium.android_webview.renderer_priority.RendererPriority.RendererPriorityEnum; import org.chromium.base.LocaleUtils; import org.chromium.base.Log; import org.chromium.base.ObserverList; @@ -1175,7 +1176,8 @@ @CalledByNative private boolean onRenderProcessGoneDetail(int childProcessID, boolean crashed) { if (isDestroyed(NO_WARN)) return false; - return mContentsClient.onRenderProcessGone(new AwRenderProcessGoneDetail(crashed)); + return mContentsClient.onRenderProcessGone(new AwRenderProcessGoneDetail( + crashed, nativeGetRendererCurrentPriority(mNativeAwContents))); } private boolean isNoOperation() { @@ -2699,6 +2701,20 @@ return mIsPopupWindow; } + @RendererPriorityEnum + public int getRendererRequestedPriority() { + return nativeGetRendererRequestedPriority(mNativeAwContents); + } + + public boolean getRendererPriorityWaivedWhenNotVisible() { + return nativeGetRendererPriorityWaivedWhenNotVisible(mNativeAwContents); + } + + public void setRendererPriorityPolicy( + @RendererPriorityEnum int rendererRequestedPriority, boolean waivedWhenNotVisible) { + nativeSetRendererPriorityPolicy( + mNativeAwContents, rendererRequestedPriority, waivedWhenNotVisible); + } //-------------------------------------------------------------------------------------------- // Methods called from native via JNI //-------------------------------------------------------------------------------------------- @@ -3436,6 +3452,12 @@ private native void nativeInvokeGeolocationCallback( long nativeAwContents, boolean value, String requestingFrame); + private native int nativeGetRendererRequestedPriority(long nativeAwContents); + private native boolean nativeGetRendererPriorityWaivedWhenNotVisible(long nativeAwContents); + private native int nativeGetRendererCurrentPriority(long nativeAwContents); + private native void nativeSetRendererPriorityPolicy( + long nativeAwContents, int rendererRequestedPriority, boolean waivedWhenNotVisible); + private native void nativeSetJsOnlineProperty(long nativeAwContents, boolean networkUp); private native void nativeTrimMemory(long nativeAwContents, int level, boolean visible);
diff --git a/android_webview/java/src/org/chromium/android_webview/AwRenderProcessGoneDetail.java b/android_webview/java/src/org/chromium/android_webview/AwRenderProcessGoneDetail.java index 5b551db0..8bd4d65 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwRenderProcessGoneDetail.java +++ b/android_webview/java/src/org/chromium/android_webview/AwRenderProcessGoneDetail.java
@@ -4,18 +4,27 @@ package org.chromium.android_webview; +import org.chromium.android_webview.renderer_priority.RendererPriority.RendererPriorityEnum; /** * This class provides more specific information about why the render process * exited. It is peer of android.webkit.RenderProcessGoneDetail. */ public class AwRenderProcessGoneDetail { private final boolean mDidCrash; + @RendererPriorityEnum + private final int mRendererPriority; - public AwRenderProcessGoneDetail(boolean didCrash) { + public AwRenderProcessGoneDetail(boolean didCrash, @RendererPriorityEnum int rendererPriority) { mDidCrash = didCrash; + mRendererPriority = rendererPriority; } public boolean didCrash() { return mDidCrash; } + + @RendererPriorityEnum + public int rendererPriority() { + return mRendererPriority; + } }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwRendererPriorityManager.java b/android_webview/java/src/org/chromium/android_webview/AwRendererPriorityManager.java new file mode 100644 index 0000000..96db506c --- /dev/null +++ b/android_webview/java/src/org/chromium/android_webview/AwRendererPriorityManager.java
@@ -0,0 +1,24 @@ +// Copyright 2017 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 org.chromium.android_webview.renderer_priority.RendererPriority; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; +import org.chromium.content.browser.ChildProcessLauncher; + +/** + * Exposes an interface via which native code can manage the priority + * of a renderer process. + */ +@JNINamespace("android_webview") +public class AwRendererPriorityManager { + @CalledByNative + private static void setRendererPriority( + int pid, @RendererPriority.RendererPriorityEnum int rendererPriority) { + // TODO(tobiasjs): handle RendererPriority.LOW separately from WAIVED. + ChildProcessLauncher.setInForeground(pid, rendererPriority == RendererPriority.HIGH); + } +}
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java index fac36c4..a475c49 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
@@ -22,6 +22,7 @@ import org.chromium.android_webview.AwContents; import org.chromium.android_webview.AwSettings; import org.chromium.android_webview.AwSwitches; +import org.chromium.android_webview.renderer_priority.RendererPriority; import org.chromium.android_webview.test.TestAwContentsClient.OnDownloadStartHelper; import org.chromium.android_webview.test.util.CommonResources; import org.chromium.android_webview.test.util.JSUtils; @@ -553,17 +554,46 @@ private static class MockBindingManager implements BindingManager { private boolean mIsChildProcessCreated; + private Object mForegroundStateLock; + private ArrayList<Boolean> mForegroundState; + + public MockBindingManager() { + super(); + mForegroundStateLock = new Object(); + mForegroundState = new ArrayList<Boolean>(); + } + boolean isChildProcessCreated() { return mIsChildProcessCreated; } + void assertSetInForegroundCall(boolean inForeground) { + synchronized (mForegroundStateLock) { + if (mForegroundState.size() == 0) { + try { + mForegroundStateLock.wait(WAIT_TIMEOUT_MS); + } catch (InterruptedException e) { + } + } + assertTrue(mForegroundState.size() != 0); + assertEquals(inForeground, mForegroundState.get(0).booleanValue()); + mForegroundState.remove(0); + return; + } + } + @Override public void addNewConnection(int pid, ChildProcessConnection connection) { mIsChildProcessCreated = true; } @Override - public void setInForeground(int pid, boolean inForeground) {} + public void setInForeground(int pid, boolean inForeground) { + synchronized (mForegroundStateLock) { + mForegroundState.add(inForeground); + mForegroundStateLock.notifyAll(); + } + } @Override public void determinedVisibility(int pid) {} @@ -618,4 +648,94 @@ mContentsClient.getOnEvaluateJavaScriptResultHelper(), "21 + 21")); } + /** + * By default the renderer should be considererd to be in the + * foreground. + */ + @Feature({"AndroidWebView"}) + @SmallTest + @CommandLineFlags + .Add(AwSwitches.WEBVIEW_SANDBOXED_RENDERER) + @ParameterizedTest.Set + public void testRendererPriorityStartsHigh() throws Throwable { + MockBindingManager bindingManager = new MockBindingManager(); + ChildProcessLauncher.setBindingManagerForTesting(bindingManager); + assertFalse(bindingManager.isChildProcessCreated()); + + AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient); + AwContents awContents = testView.getAwContents(); + loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), "<html></html>", + "text/html", false); + + assertTrue(bindingManager.isChildProcessCreated()); + bindingManager.assertSetInForegroundCall(true); + } + + /** + * If we specify that the priority is WAIVED, then the renderer + * should not be in the foreground. + */ + @Feature({"AndroidWebView"}) + @SmallTest + @CommandLineFlags + .Add(AwSwitches.WEBVIEW_SANDBOXED_RENDERER) + @ParameterizedTest.Set + public void testRendererPriorityLow() throws Throwable { + MockBindingManager bindingManager = new MockBindingManager(); + ChildProcessLauncher.setBindingManagerForTesting(bindingManager); + assertFalse(bindingManager.isChildProcessCreated()); + + final AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient); + final AwContents awContents = testView.getAwContents(); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + awContents.setRendererPriorityPolicy(RendererPriority.WAIVED, false); + } + }); + loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), "<html></html>", + "text/html", false); + + assertTrue(awContents.isPageVisible()); + assertTrue(bindingManager.isChildProcessCreated()); + bindingManager.assertSetInForegroundCall(false); + } + + /** + * If we specify that the priority is HIGH, but WAIVED when in the + * background, then pausing the view should send the renderer to + * the background. + */ + @Feature({"AndroidWebView"}) + @SmallTest + @CommandLineFlags + .Add(AwSwitches.WEBVIEW_SANDBOXED_RENDERER) + @ParameterizedTest.Set + public void testRendererPriorityManaged() throws Throwable { + MockBindingManager bindingManager = new MockBindingManager(); + ChildProcessLauncher.setBindingManagerForTesting(bindingManager); + assertFalse(bindingManager.isChildProcessCreated()); + + final AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient); + final AwContents awContents = testView.getAwContents(); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + awContents.setRendererPriorityPolicy(RendererPriority.HIGH, true); + } + }); + loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), "<html></html>", + "text/html", false); + + assertTrue(awContents.isPageVisible()); + assertTrue(bindingManager.isChildProcessCreated()); + bindingManager.assertSetInForegroundCall(true); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + awContents.onPause(); + } + }); + bindingManager.assertSetInForegroundCall(false); + } }
diff --git a/android_webview/lib/main/aw_main_delegate.cc b/android_webview/lib/main/aw_main_delegate.cc index 84c98fd..9b9848ed 100644 --- a/android_webview/lib/main/aw_main_delegate.cc +++ b/android_webview/lib/main/aw_main_delegate.cc
@@ -147,7 +147,7 @@ if (cl->HasSwitch(switches::kWebViewSandboxedRenderer)) { cl->AppendSwitch(switches::kInProcessGPU); cl->AppendSwitchASCII(switches::kRendererProcessLimit, "1"); - cl->AppendSwitch(switches::kDisableRendererBackgrounding); + cl->AppendSwitch(switches::kDisableRendererPriorityManagement); } CommandLineHelper::AddEnabledFeature(
diff --git a/android_webview/native/BUILD.gn b/android_webview/native/BUILD.gn index 2b2d04e6..4efadee5 100644 --- a/android_webview/native/BUILD.gn +++ b/android_webview/native/BUILD.gn
@@ -76,6 +76,8 @@ "aw_picture.h", "aw_quota_manager_bridge_impl.cc", "aw_quota_manager_bridge_impl.h", + "aw_renderer_priority_manager.cc", + "aw_renderer_priority_manager.h", "aw_resource.cc", "aw_settings.cc", "aw_settings.h", @@ -141,6 +143,7 @@ "../java/src/org/chromium/android_webview/AwPdfExporter.java", "../java/src/org/chromium/android_webview/AwPicture.java", "../java/src/org/chromium/android_webview/AwQuotaManagerBridge.java", + "../java/src/org/chromium/android_webview/AwRendererPriorityManager.java", "../java/src/org/chromium/android_webview/AwResource.java", "../java/src/org/chromium/android_webview/AwSettings.java", "../java/src/org/chromium/android_webview/AwTokenBindingManager.java", @@ -168,3 +171,9 @@ "permission/aw_permission_request.h", ] } + +java_cpp_enum("aw_renderer_priority_manager_renderer_priority") { + sources = [ + "aw_renderer_priority_manager.h", + ] +}
diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc index dfd0326..050d715 100644 --- a/android_webview/native/aw_contents.cc +++ b/android_webview/native/aw_contents.cc
@@ -27,6 +27,7 @@ #include "android_webview/native/aw_gl_functor.h" #include "android_webview/native/aw_pdf_exporter.h" #include "android_webview/native/aw_picture.h" +#include "android_webview/native/aw_renderer_priority_manager.h" #include "android_webview/native/aw_web_contents_delegate.h" #include "android_webview/native/java_browser_view_renderer_helper.h" #include "android_webview/native/permission/aw_permission_request.h" @@ -67,6 +68,8 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host.h" +#include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/ssl_status.h" #include "content/public/browser/web_contents.h" #include "content/public/common/mhtml_generation_params.h" @@ -79,7 +82,6 @@ #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/image/image.h" - struct AwDrawSWFunctionTable; using autofill::ContentAutofillDriverFactory; @@ -112,6 +114,8 @@ std::string g_locale_list; const void* kAwContentsUserDataKey = &kAwContentsUserDataKey; +const void* kComputedRendererPriorityUserDataKey = + &kComputedRendererPriorityUserDataKey; class AwContentsUserData : public base::SupportsUserData::Data { public: @@ -199,7 +203,10 @@ this, BrowserThread::GetTaskRunnerForThread(BrowserThread::UI)), web_contents_(std::move(web_contents)), - renderer_manager_key_(GLViewRendererManager::GetInstance()->NullKey()) { + renderer_manager_key_(GLViewRendererManager::GetInstance()->NullKey()), + renderer_requested_priority_( + AwRendererPriorityManager::RENDERER_PRIORITY_HIGH), + renderer_priority_waived_when_not_visible_(false) { base::subtle::NoBarrier_AtomicIncrement(&g_instance_count, 1); icon_helper_.reset(new IconHelper(web_contents_.get())); icon_helper_->SetListener(this); @@ -228,6 +235,8 @@ InitAutofillIfNecessary(autofill_manager_delegate->GetSaveFormData()); content::SynchronousCompositor::SetClientForWebContents( web_contents_.get(), &browser_view_renderer_); + UpdateRendererPriority(); + web_contents_->GetRenderProcessHost()->AddObserver(this); AwContentsLifecycleNotifier::OnWebViewCreated(); } @@ -307,6 +316,8 @@ AwContents::~AwContents() { DCHECK_EQ(this, AwContents::FromWebContents(web_contents_.get())); + web_contents_->GetRenderProcessHost()->RemoveObserver(this); + UpdateRendererPriority(AwRendererPriorityManager::RENDERER_PRIORITY_WAIVED); web_contents_->RemoveUserData(kAwContentsUserDataKey); if (find_helper_.get()) find_helper_->SetListener(NULL); @@ -859,6 +870,7 @@ bool visible) { DCHECK_CURRENTLY_ON(BrowserThread::UI); browser_view_renderer_.SetViewVisibility(visible); + UpdateRendererPriority(); } void AwContents::SetWindowVisibility(JNIEnv* env, @@ -866,6 +878,7 @@ bool visible) { DCHECK_CURRENTLY_ON(BrowserThread::UI); browser_view_renderer_.SetWindowVisibility(visible); + UpdateRendererPriority(); } void AwContents::SetIsPaused(JNIEnv* env, @@ -873,6 +886,7 @@ bool paused) { DCHECK_CURRENTLY_ON(BrowserThread::UI); browser_view_renderer_.SetIsPaused(paused); + UpdateRendererPriority(); } void AwContents::OnAttachedToWindow(JNIEnv* env, @@ -881,12 +895,14 @@ int h) { DCHECK_CURRENTLY_ON(BrowserThread::UI); browser_view_renderer_.OnAttachedToWindow(w, h); + UpdateRendererPriority(); } void AwContents::OnDetachedFromWindow(JNIEnv* env, const JavaParamRef<jobject>& obj) { DCHECK_CURRENTLY_ON(BrowserThread::UI); browser_view_renderer_.OnDetachedFromWindow(); + UpdateRendererPriority(); } bool AwContents::IsVisible(JNIEnv* env, const JavaParamRef<jobject>& obj) { @@ -1173,6 +1189,91 @@ ScopedJavaGlobalRef<jobject>(env, callback))); } +void AwContents::UpdateRendererPriority( + AwRendererPriorityManager::RendererPriority base_priority) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + content::RenderProcessHost* rph = web_contents_->GetRenderProcessHost(); + AwRendererPriorityManager::RendererPriority computed_priority = base_priority; + + std::unique_ptr<content::RenderWidgetHostIterator> widgets( + content::RenderWidgetHost::GetRenderWidgetHosts()); + content::RenderWidgetHost* widget; + while ((widget = widgets->GetNextHost()) != nullptr && + computed_priority < + AwRendererPriorityManager::RENDERER_PRIORITY_HIGH) { + content::RenderViewHost* view = content::RenderViewHost::From(widget); + if (view && rph == view->GetProcess()) { + content::WebContents* wc = content::WebContents::FromRenderViewHost(view); + if (wc && wc != web_contents_.get()) { + computed_priority = + std::max(FromWebContents(wc)->GetComputedRendererPriority(), + computed_priority); + } + } + } + GetAwRendererPriorityManager()->SetRendererPriority(computed_priority); +} + +AwRendererPriorityManager::RendererPriority +AwContents::GetComputedRendererPriority() { + if (renderer_priority_waived_when_not_visible_ && + !browser_view_renderer_.IsClientVisible()) { + return AwRendererPriorityManager::RENDERER_PRIORITY_WAIVED; + } + return renderer_requested_priority_; +} + +void AwContents::UpdateRendererPriority() { + UpdateRendererPriority(GetComputedRendererPriority()); +} + +AwRendererPriorityManager* AwContents::GetAwRendererPriorityManager() { + content::RenderProcessHost* rph = web_contents_->GetRenderProcessHost(); + AwRendererPriorityManager* manager = static_cast<AwRendererPriorityManager*>( + rph->GetUserData(kComputedRendererPriorityUserDataKey)); + if (manager == nullptr) { + rph->SetUserData(kComputedRendererPriorityUserDataKey, + manager = new AwRendererPriorityManager(rph)); + } + return manager; +} + +AwRendererPriorityManager::RendererPriority +AwContents::GetCurrentRendererPriority() { + return GetAwRendererPriorityManager()->GetRendererPriority(); +} + +jint AwContents::GetRendererCurrentPriority( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj) { + return GetCurrentRendererPriority(); +} + +jint AwContents::GetRendererRequestedPriority( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj) { + return renderer_requested_priority_; +} + +jboolean AwContents::GetRendererPriorityWaivedWhenNotVisible( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj) { + return renderer_priority_waived_when_not_visible_; +} + +void AwContents::SetRendererPriorityPolicy( + JNIEnv* env, + const JavaParamRef<jobject>& obj, + jint renderer_requested_priority, + jboolean renderer_priority_waived_when_not_visible) { + renderer_requested_priority_ = + static_cast<AwRendererPriorityManager::RendererPriority>( + renderer_requested_priority); + renderer_priority_waived_when_not_visible_ = + renderer_priority_waived_when_not_visible; + UpdateRendererPriority(renderer_requested_priority_); +} + void AwContents::ClearView(JNIEnv* env, const JavaParamRef<jobject>& obj) { DCHECK_CURRENTLY_ON(BrowserThread::UI); browser_view_renderer_.ClearView(); @@ -1343,4 +1444,8 @@ child_process_id, crashed); } +void AwContents::RenderProcessReady(content::RenderProcessHost* host) { + UpdateRendererPriority(); +} + } // namespace android_webview
diff --git a/android_webview/native/aw_contents.h b/android_webview/native/aw_contents.h index 761a723..fefaefb4 100644 --- a/android_webview/native/aw_contents.h +++ b/android_webview/native/aw_contents.h
@@ -23,11 +23,13 @@ #include "android_webview/browser/render_thread_manager.h" #include "android_webview/browser/render_thread_manager_client.h" #include "android_webview/browser/renderer_host/aw_render_view_host_ext.h" +#include "android_webview/native/aw_renderer_priority_manager.h" #include "android_webview/native/permission/permission_request_handler_client.h" #include "base/android/jni_weak_ref.h" #include "base/android/scoped_java_ref.h" #include "base/callback_forward.h" #include "base/macros.h" +#include "content/public/browser/render_process_host_observer.h" #include "content/public/browser/web_contents_observer.h" class SkBitmap; @@ -67,6 +69,7 @@ public AwBrowserPermissionRequestDelegate, public AwRenderProcessGoneDelegate, public content::WebContentsObserver, + public content::RenderProcessHostObserver, public AwSafeBrowsingUIManager::UIManagerClient { public: // Returns the AwContents instance associated with |web_contents|, or NULL. @@ -213,6 +216,22 @@ jboolean value, const base::android::JavaParamRef<jstring>& origin); + AwRendererPriorityManager::RendererPriority GetCurrentRendererPriority(); + jint GetRendererCurrentPriority( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj); + jint GetRendererRequestedPriority( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj); + jboolean GetRendererPriorityWaivedWhenNotVisible( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj); + void SetRendererPriorityPolicy( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj, + jint rendererRequestedPriority, + jboolean waivedhenNotVisible); + // PermissionRequestHandlerClient implementation. void OnPermissionRequest(base::android::ScopedJavaLocalRef<jobject> j_request, AwPermissionRequest* request) override; @@ -343,6 +362,9 @@ void DidAttachInterstitialPage() override; void DidDetachInterstitialPage() override; + // content::RenderProcessHostObserver overrides + void RenderProcessReady(content::RenderProcessHost* host) override; + // AwSafeBrowsingUIManager::UIManagerClient implementation bool CanShowInterstitial() override; @@ -361,6 +383,12 @@ void SetAwGLFunctor(AwGLFunctor* functor); + AwRendererPriorityManager* GetAwRendererPriorityManager(); + AwRendererPriorityManager::RendererPriority GetComputedRendererPriority(); + void UpdateRendererPriority( + AwRendererPriorityManager::RendererPriority base_priority); + void UpdateRendererPriority(); + JavaObjectWeakGlobalRef java_ref_; AwGLFunctor* functor_; BrowserViewRenderer browser_view_renderer_; // Must outlive |web_contents_|. @@ -377,12 +405,15 @@ // GURL is supplied by the content layer as requesting frame. // Callback is supplied by the content layer, and is invoked with the result // from the permission prompt. - typedef std::pair<const GURL, base::Callback<void(bool)> > OriginCallback; + typedef std::pair<const GURL, base::Callback<void(bool)>> OriginCallback; // The first element in the list is always the currently pending request. std::list<OriginCallback> pending_geolocation_prompts_; GLViewRendererManager::Key renderer_manager_key_; + AwRendererPriorityManager::RendererPriority renderer_requested_priority_; + bool renderer_priority_waived_when_not_visible_; + DISALLOW_COPY_AND_ASSIGN(AwContents); };
diff --git a/android_webview/native/aw_renderer_priority_manager.cc b/android_webview/native/aw_renderer_priority_manager.cc new file mode 100644 index 0000000..c19f323 --- /dev/null +++ b/android_webview/native/aw_renderer_priority_manager.cc
@@ -0,0 +1,42 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "android_webview/native/aw_renderer_priority_manager.h" + +#include "base/android/jni_android.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_process_host.h" +#include "jni/AwRendererPriorityManager_jni.h" + +namespace android_webview { + +AwRendererPriorityManager::AwRendererPriorityManager( + content::RenderProcessHost* host) + : host_(host), renderer_priority_(RENDERER_PRIORITY_INITIAL) {} + +void AwRendererPriorityManager::SetRendererPriority( + RendererPriority renderer_priority) { + if (host_->GetHandle() == 0) { + return; + } + if (renderer_priority_ != renderer_priority) { + renderer_priority_ = renderer_priority; + content::BrowserThread::PostTask( + content::BrowserThread::PROCESS_LAUNCHER, FROM_HERE, + base::Bind(&SetRendererPriorityOnLauncherThread, host_->GetHandle(), + renderer_priority_)); + } +} + +// static +void AwRendererPriorityManager::SetRendererPriorityOnLauncherThread( + int pid, + RendererPriority renderer_priority) { + JNIEnv* env = base::android::AttachCurrentThread(); + DCHECK(env); + Java_AwRendererPriorityManager_setRendererPriority( + env, static_cast<jint>(pid), static_cast<jint>(renderer_priority)); +} + +} // namespace android_webview
diff --git a/android_webview/native/aw_renderer_priority_manager.h b/android_webview/native/aw_renderer_priority_manager.h new file mode 100644 index 0000000..dec22415 --- /dev/null +++ b/android_webview/native/aw_renderer_priority_manager.h
@@ -0,0 +1,42 @@ +// Copyright 2017 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_RENDERER_PRIORITY_USER_DATA_H_ +#define ANDROID_WEBVIEW_NATIVE_AW_RENDERER_PRIORITY_USER_DATA_H_ + +#include "base/supports_user_data.h" + +namespace content { +class RenderProcessHost; +} + +namespace android_webview { + +class AwRendererPriorityManager : public base::SupportsUserData::Data { + public: + // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.android_webview.renderer_priority + enum RendererPriority { + RENDERER_PRIORITY_INITIAL = -1, + RENDERER_PRIORITY_WAIVED = 0, + RENDERER_PRIORITY_LOW = 1, + RENDERER_PRIORITY_HIGH = 2 + }; + + explicit AwRendererPriorityManager(content::RenderProcessHost* host); + + RendererPriority GetRendererPriority() const { return renderer_priority_; } + void SetRendererPriority(RendererPriority renderer_priority); + + private: + static void SetRendererPriorityOnLauncherThread( + int pid, + RendererPriority renderer_priority); + + content::RenderProcessHost* host_; + RendererPriority renderer_priority_; +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_NATIVE_AW_RENDERER_PRIORITY_USER_DATA_H_
diff --git a/build/whitespace_file.txt b/build/whitespace_file.txt index 5327d72..e9b2b0f 100644 --- a/build/whitespace_file.txt +++ b/build/whitespace_file.txt
@@ -162,4 +162,4 @@ No, really, I couldn't eat another bit. When I hunger I think of you, and a pastrami sandwich. Do make a terrible mistake every once in a while. -I just made one. +I just made two.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java index 9823c1e..79344e1f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/ChildNode.java
@@ -22,6 +22,13 @@ mParent = parent; } + @Override + @CallSuper + public void detach() { + assert mParent != null; + mParent = null; + } + protected void notifyItemRangeChanged(int index, int count, Object payload) { if (mParent != null) mParent.onItemRangeChanged(this, index, count, payload); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java index fcb153e..2b9d1f3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/InnerNode.java
@@ -158,6 +158,7 @@ int count = child.getItemCount(); int childStartingOffset = getStartingOffsetForChildIndex(removedIndex); + child.detach(); mChildren.remove(removedIndex); if (count > 0) notifyItemRangeRemoved(childStartingOffset, count); } @@ -169,6 +170,7 @@ int itemCount = getItemCount(); if (itemCount == 0) return; + for (TreeNode child : mChildren) child.detach(); mChildren.clear(); notifyItemRangeRemoved(0, itemCount); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java index da91464..4d090a9b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
@@ -6,6 +6,7 @@ import org.chromium.base.Log; import org.chromium.base.VisibleForTesting; +import org.chromium.chrome.browser.ntp.NewTabPage.DestructionObserver; import org.chromium.chrome.browser.ntp.snippets.CategoryInt; import org.chromium.chrome.browser.ntp.snippets.CategoryStatus; import org.chromium.chrome.browser.ntp.snippets.CategoryStatus.CategoryStatusEnum; @@ -42,6 +43,13 @@ mUiDelegate.getMetricsReporter().setRanker(mSuggestionsRanker); mOfflinePageBridge = offlinePageBridge; resetSections(/* alwaysAllowEmptySections = */ false); + + mUiDelegate.addDestructionObserver(new DestructionObserver() { + @Override + public void onDestroy() { + removeAllSections(); + } + }); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java index 850fec8..c9a636a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSection.java
@@ -4,9 +4,10 @@ package org.chromium.chrome.browser.ntp.cards; +import android.support.annotation.CallSuper; + import org.chromium.base.Callback; import org.chromium.base.Log; -import org.chromium.chrome.browser.ntp.NewTabPage.DestructionObserver; import org.chromium.chrome.browser.ntp.NewTabPageUma; import org.chromium.chrome.browser.ntp.snippets.CategoryInt; import org.chromium.chrome.browser.ntp.snippets.CategoryStatus.CategoryStatusEnum; @@ -46,6 +47,7 @@ private final Delegate mDelegate; private final SuggestionsCategoryInfo mCategoryInfo; private final OfflinePageBridge mOfflinePageBridge; + private final OfflinePageBridge.OfflinePageModelObserver mOfflinePageObserver; // Children private final SectionHeader mHeader; @@ -54,8 +56,6 @@ private final ActionItem mMoreButton; private final ProgressItem mProgressIndicator; - private boolean mIsNtpDestroyed; - /** * Keeps track of how many suggestions have been seen by the user so that we replace only * suggestions that have not been seen, yet. @@ -91,7 +91,34 @@ mProgressIndicator = new ProgressItem(); addChildren(mHeader, mSuggestionsList, mStatus, mMoreButton, mProgressIndicator); - setupOfflinePageBridgeObserver(uiDelegate); + mOfflinePageObserver = + new OfflinePageBridge.OfflinePageModelObserver() { + @Override + public void offlinePageModelLoaded() { + updateAllSnippetOfflineAvailability(); + } + + @Override + public void offlinePageAdded(OfflinePageItem addedPage) { + updateAllSnippetOfflineAvailability(); + } + + @Override + public void offlinePageDeleted(long offlineId, ClientId clientId) { + for (SnippetArticle article : mSuggestionsList) { + if (article.requiresExactOfflinePage()) continue; + Long articleOfflineId = article.getOfflinePageOfflineId(); + if (articleOfflineId == null) continue; + if (articleOfflineId.longValue() != offlineId) continue; + // The old value cannot be simply removed without a request to the + // model, because there may be an older offline page for the same + // URL. + updateSnippetOfflineAvailability(article); + } + } + }; + mOfflinePageBridge.addObserver(mOfflinePageObserver); + refreshChildrenVisibility(); } @@ -210,43 +237,11 @@ } } - private void setupOfflinePageBridgeObserver(SuggestionsUiDelegate uiDelegate) { - final OfflinePageBridge.OfflinePageModelObserver observer = - new OfflinePageBridge.OfflinePageModelObserver() { - @Override - public void offlinePageModelLoaded() { - updateAllSnippetOfflineAvailability(); - } - - @Override - public void offlinePageAdded(OfflinePageItem addedPage) { - updateAllSnippetOfflineAvailability(); - } - - @Override - public void offlinePageDeleted(long offlineId, ClientId clientId) { - for (SnippetArticle article : mSuggestionsList) { - if (article.requiresExactOfflinePage()) continue; - Long articleOfflineId = article.getOfflinePageOfflineId(); - if (articleOfflineId == null) continue; - if (articleOfflineId.longValue() != offlineId) continue; - // The old value cannot be simply removed without a request to the - // model, because there may be an older offline page for the same - // URL. - updateSnippetOfflineAvailability(article); - } - } - }; - - mOfflinePageBridge.addObserver(observer); - - uiDelegate.addDestructionObserver(new DestructionObserver() { - @Override - public void onDestroy() { - mIsNtpDestroyed = true; - mOfflinePageBridge.removeObserver(observer); - } - }); + @Override + @CallSuper + public void detach() { + mOfflinePageBridge.removeObserver(mOfflinePageObserver); + super.detach(); } private void refreshChildrenVisibility() { @@ -406,7 +401,6 @@ article.mUrl, /*tabId=*/0, new Callback<OfflinePageItem>() { @Override public void onResult(OfflinePageItem item) { - if (mIsNtpDestroyed) return; mSuggestionsList.updateSuggestionOfflineId( article, item == null ? null : item.getOfflineId()); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java index 7a13f6f..ff7aef1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/TreeNode.java
@@ -21,6 +21,12 @@ void setParent(NodeParent parent); /** + * Detaches the node from the parent so that changes in the node are no longer notified to the + * parent. This is needed when the parent removes this node from its children. + */ + void detach(); + + /** * Returns the number of items under this subtree. This method may be called * before initialization. *
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java index 4d1596e..54ffe63 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/InnerNodeTest.java
@@ -135,16 +135,27 @@ // The parent should have been notified about the removed items. verify(mParent).onItemRangeRemoved(mInnerNode, 6, 3); + verify(child).detach(); reset(mParent); // Prepare for the #verifyNoMoreInteractions() call below. TreeNode child2 = mChildren.get(3); mInnerNode.removeChild(child2); + verify(child2).detach(); // There should be no change notifications about the empty child. verifyNoMoreInteractions(mParent); } @Test + public void testRemoveChildren() { + mInnerNode.removeChildren(); + + // The parent should have been notified about the removed items. + verify(mParent).onItemRangeRemoved(mInnerNode, 0, 12); + for (TreeNode child : mChildren) verify(child).detach(); + } + + @Test public void testNotifications() { mInnerNode.onItemRangeInserted(mChildren.get(0), 0, 23); mInnerNode.onItemRangeChanged(mChildren.get(2), 2, 9000, null);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java index e8d0cad..4b0155cb 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java
@@ -5,8 +5,12 @@ package org.chromium.chrome.browser.ntp.cards; import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.chromium.chrome.browser.ntp.cards.ContentSuggestionsTestUtils.bindViewHolders; @@ -17,6 +21,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.annotation.Config; @@ -24,6 +29,7 @@ import org.chromium.base.Callback; import org.chromium.base.test.util.Feature; import org.chromium.chrome.browser.DisableHistogramsRule; +import org.chromium.chrome.browser.ntp.NewTabPage.DestructionObserver; import org.chromium.chrome.browser.ntp.cards.ContentSuggestionsTestUtils.CategoryInfoBuilder; import org.chromium.chrome.browser.ntp.snippets.FakeSuggestionsSource; import org.chromium.chrome.browser.ntp.snippets.KnownCategories; @@ -233,4 +239,30 @@ .getPerSectionRank(), equalTo(3)); } + + @Test + @Feature({"Ntp"}) + public void testRemovesSectionsWhenUiDelegateDestroyed() { + registerCategory(mSuggestionSource, KnownCategories.ARTICLES, 1); + registerCategory(mSuggestionSource, + new CategoryInfoBuilder(KnownCategories.DOWNLOADS).withViewAllAction().build(), 3); + SectionList sectionList = new SectionList(mUiDelegate, mOfflinePageBridge); + bindViewHolders(sectionList); + + ArgumentCaptor<DestructionObserver> argument = + ArgumentCaptor.forClass(DestructionObserver.class); + verify(mUiDelegate).addDestructionObserver(argument.capture()); + + assertFalse(sectionList.isEmpty()); + SuggestionsSection section = sectionList.getSectionForTesting(KnownCategories.ARTICLES); + assertNotNull(section); + + // Now destroy the UI and thus notify the SectionList. + argument.getValue().onDestroy(); + // The section should be removed. + assertTrue(sectionList.isEmpty()); + // Verify that the section has been detached by notifying its parent about changes. If not + // detached, it should crash. + section.notifyItemRangeChanged(0, 1); + } }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java index 4450d7f..7b13c4a 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SuggestionsSectionTest.java
@@ -302,6 +302,36 @@ @Test @Feature({"Ntp"}) + public void testOfflineStatusIgnoredIfDetached() { + final int suggestionCount = 2; + final List<SnippetArticle> suggestions = createDummySuggestions(suggestionCount); + assertNull(suggestions.get(0).getOfflinePageOfflineId()); + assertNull(suggestions.get(1).getOfflinePageOfflineId()); + + final OfflinePageItem item0 = createOfflinePageItem(suggestions.get(0).mUrl, 0L); + mBridge.setIsOfflinePageModelLoaded(true); + mBridge.setItems(Arrays.asList(item0)); + + SuggestionsSection section = createSectionWithSuggestions(suggestions); + + // The offline status should propagate before detaching. + assertEquals(Long.valueOf(0L), suggestions.get(0).getOfflinePageOfflineId()); + assertNull(suggestions.get(1).getOfflinePageOfflineId()); + + section.detach(); + + final OfflinePageItem item1 = createOfflinePageItem(suggestions.get(1).mUrl, 1L); + mBridge.setItems(Arrays.asList(item0, item1)); + // Check that a change in OfflinePageBridge state forces an update. + mBridge.fireOfflinePageModelLoaded(); + + // The offline status should not change any more. + assertEquals(Long.valueOf(0L), suggestions.get(0).getOfflinePageOfflineId()); + assertNull(suggestions.get(1).getOfflinePageOfflineId()); + } + + @Test + @Feature({"Ntp"}) public void testViewAllActionPriority() { // When all the actions are enabled, ViewAll always has the priority and is shown.
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 33250439..443d291 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -3673,6 +3673,8 @@ "supervised_user/child_accounts/child_account_service_factory.h", "supervised_user/child_accounts/family_info_fetcher.cc", "supervised_user/child_accounts/family_info_fetcher.h", + "supervised_user/child_accounts/kids_management_api.cc", + "supervised_user/child_accounts/kids_management_api.h", "supervised_user/child_accounts/permission_request_creator_apiary.cc", "supervised_user/child_accounts/permission_request_creator_apiary.h", "supervised_user/experimental/safe_search_url_reporter.cc",
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root.cc b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root.cc index 1eb533f..8264bcad 100644 --- a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root.cc +++ b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root.cc
@@ -18,7 +18,6 @@ #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h" #include "chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.h" #include "content/public/browser/browser_thread.h" -#include "net/base/mime_util.h" #include "url/gurl.h" using content::BrowserThread; @@ -53,17 +52,13 @@ base::ToLowerASCII(base::FilePath(filename).Extension()); if (!extension.empty()) extension = extension.substr(1); // Strip the leading dot. - std::vector<base::FilePath::StringType> possible_extensions; - net::GetExtensionsForMimeType(document->mime_type, &possible_extensions); + std::vector<base::FilePath::StringType> possible_extensions = + GetExtensionsForArcMimeType(document->mime_type); if (!possible_extensions.empty() && std::find(possible_extensions.begin(), possible_extensions.end(), extension) == possible_extensions.end()) { - base::FilePath::StringType new_extension; - if (!net::GetPreferredExtensionForMimeType(document->mime_type, - &new_extension)) { - new_extension = possible_extensions[0]; - } - filename = base::FilePath(filename).AddExtension(new_extension).value(); + filename = + base::FilePath(filename).AddExtension(possible_extensions[0]).value(); } return filename;
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.cc b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.cc index 26c3db5..488265b 100644 --- a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.cc +++ b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.cc
@@ -4,16 +4,92 @@ #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h" -#include <vector> +#include <algorithm> +#include <utility> +#include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "net/base/escape.h" +#include "net/base/mime_util.h" #include "storage/browser/fileapi/file_system_url.h" #include "url/gurl.h" namespace arc { +namespace { + +struct MimeTypeToExtensions { + const char* mime_type; + const char* extensions; +}; + +// The mapping from MIME types to file name extensions, taken from Android N. +// See: frameworks/base/media/java/android/media/MediaFile.java +constexpr MimeTypeToExtensions kAndroidMimeTypeMappings[] = { + {"application/mspowerpoint", "ppt"}, + {"application/msword", "doc"}, + {"application/ogg", "ogg,oga"}, + {"application/pdf", "pdf"}, + {"application/vnd.apple.mpegurl", "m3u8"}, + {"application/vnd.ms-excel", "xls"}, + {"application/vnd.ms-wpl", "wpl"}, + {"application/x-android-drm-fl", "fl"}, + {"application/x-mpegurl", "m3u"}, + {"application/zip", "zip"}, + {"audio/aac", "aac"}, + {"audio/aac-adts", "aac"}, + {"audio/amr", "amr"}, + {"audio/amr-wb", "awb"}, + {"audio/flac", "flac"}, + {"audio/imelody", "imy"}, + {"audio/midi", "mid,midi,xmf,rtttl,rtx,ota,mxmf"}, + {"audio/mp4", "m4a"}, + {"audio/mpeg", "mp3,mpga"}, + {"audio/mpegurl", "m3u8"}, + {"audio/ogg", "ogg"}, + {"audio/sp-midi", "smf"}, + {"audio/x-matroska", "mka"}, + {"audio/x-mpegurl", "m3u,m3u8"}, + {"audio/x-ms-wma", "wma"}, + {"audio/x-scpls", "pls"}, + {"audio/x-wav", "wav"}, + {"image/gif", "gif"}, + {"image/jpeg", "jpg,jpeg"}, + {"image/png", "png"}, + {"image/vnd.wap.wbmp", "wbmp"}, + {"image/webp", "webp"}, + {"image/x-adobe-dng", "dng"}, + {"image/x-canon-cr2", "cr2"}, + {"image/x-fuji-raf", "raf"}, + {"image/x-ms-bmp", "bmp"}, + {"image/x-nikon-nef", "nef"}, + {"image/x-nikon-nrw", "nrw"}, + {"image/x-olympus-orf", "orf"}, + {"image/x-panasonic-rw2", "rw2"}, + {"image/x-pentax-pef", "pef"}, + {"image/x-samsung-srw", "srw"}, + {"image/x-sony-arw", "arw"}, + {"text/html", "html,htm"}, + {"text/plain", "txt"}, + {"video/3gpp", "3gp,3gpp"}, + {"video/3gpp2", "3g2,3gpp2"}, + {"video/avi", "avi"}, + {"video/mp2p", "mpg,mpeg"}, + {"video/mp2ts", "ts"}, + {"video/mp4", "mp4,m4v"}, + {"video/mpeg", "mpg,mpeg"}, + {"video/quicktime", "mov"}, + {"video/webm", "webm"}, + {"video/x-matroska", "mkv"}, + {"video/x-ms-asf", "asf"}, + {"video/x-ms-wmv", "wmv"}, +}; + +constexpr char kApplicationOctetStreamMimeType[] = "application/octet-stream"; + +} // namespace + // This is based on net/base/escape.cc: net::(anonymous namespace)::Escape. // TODO(nya): Consider consolidating this function with EscapeFileSystemId() in // chrome/browser/chromeos/file_system_provider/mount_path_util.cc. @@ -99,4 +175,45 @@ net::EscapeQueryParamValue(document_id, false /* use_plus */).c_str())); } +std::vector<base::FilePath::StringType> GetExtensionsForArcMimeType( + const std::string& mime_type) { + // net::GetExtensionsForMimeType() returns unwanted extensions like + // "exe,com,bin" for application/octet-stream. + if (net::MatchesMimeType(kApplicationOctetStreamMimeType, mime_type)) + return std::vector<base::FilePath::StringType>(); + + // Attempt net::GetExtensionsForMimeType(). + { + std::vector<base::FilePath::StringType> extensions; + net::GetExtensionsForMimeType(mime_type, &extensions); + if (!extensions.empty()) { + base::FilePath::StringType preferred_extension; + if (net::GetPreferredExtensionForMimeType(mime_type, + &preferred_extension)) { + auto iter = std::find(extensions.begin(), extensions.end(), + preferred_extension); + if (iter == extensions.end()) { + // This is unlikely to happen, but there is no guarantee. + extensions.insert(extensions.begin(), preferred_extension); + } else { + std::swap(extensions.front(), *iter); + } + } + return extensions; + } + } + + // Fallback to our hard-coded list. + for (const auto& entry : kAndroidMimeTypeMappings) { + if (net::MatchesMimeType(entry.mime_type, mime_type)) { + // We assume base::FilePath::StringType == std::string as this code is + // built only on Chrome OS. + return base::SplitString(entry.extensions, ",", base::TRIM_WHITESPACE, + base::SPLIT_WANT_ALL); + } + } + + return std::vector<base::FilePath::StringType>(); +} + } // namespace arc
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h index 1ba3583..4f130dde 100644 --- a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h +++ b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h
@@ -8,6 +8,7 @@ #define CHROME_BROWSER_CHROMEOS_ARC_FILEAPI_ARC_DOCUMENTS_PROVIDER_UTIL_H_ #include <string> +#include <vector> #include "base/files/file_path.h" @@ -61,6 +62,14 @@ GURL BuildDocumentUrl(const std::string& authority, const std::string& document_id); +// Similar to net::GetExtensionsForMimeType(), but this covers more MIME types +// used in Android. +// Returns an empty vector if the MIME type is not known. +// If the returned vector is not empty, the first extension is the preferred +// extension. +std::vector<base::FilePath::StringType> GetExtensionsForArcMimeType( + const std::string& mime_type); + } // namespace arc #endif // CHROME_BROWSER_CHROMEOS_ARC_FILEAPI_ARC_DOCUMENTS_PROVIDER_UTIL_H_
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util_unittest.cc index 496bdd4e..82145ab3 100644 --- a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util_unittest.cc +++ b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util_unittest.cc
@@ -199,6 +199,28 @@ EXPECT_EQ("content://../document/..", BuildDocumentUrl("..", "..").spec()); } +TEST(ArcDocumentsProviderUtilTest, GetExtensionsForArcMimeType) { + // MIME types already known to Chromium. + EXPECT_NE(0u, GetExtensionsForArcMimeType("audio/mp3").size()); + EXPECT_NE(0u, GetExtensionsForArcMimeType("image/jpeg").size()); + EXPECT_NE(0u, GetExtensionsForArcMimeType("text/html").size()); + EXPECT_NE( + 0u, GetExtensionsForArcMimeType("application/x-chrome-extension").size()); + + // MIME types known to Android only. + EXPECT_NE(0u, + GetExtensionsForArcMimeType("application/x-android-drm-fl").size()); + EXPECT_NE(0u, GetExtensionsForArcMimeType("audio/x-wav").size()); + + // Unknown types. + EXPECT_EQ(0u, GetExtensionsForArcMimeType("abc/xyz").size()); + EXPECT_EQ( + 0u, GetExtensionsForArcMimeType("vnd.android.document/directory").size()); + + // Specially handled types. + EXPECT_EQ(0u, GetExtensionsForArcMimeType("application/octet-stream").size()); +} + } // namespace } // namespace arc
diff --git a/chrome/browser/password_manager/native_backend_gnome_x.cc b/chrome/browser/password_manager/native_backend_gnome_x.cc index 9dcf475..a51f1d3 100644 --- a/chrome/browser/password_manager/native_backend_gnome_x.cc +++ b/chrome/browser/password_manager/native_backend_gnome_x.cc
@@ -31,6 +31,7 @@ using base::UTF16ToUTF8; using content::BrowserThread; using namespace password_manager::metrics_util; +using password_manager::MatchResult; using password_manager::PasswordStore; namespace { @@ -121,45 +122,48 @@ std::vector<std::unique_ptr<PasswordForm>> forms; password_manager::PSLDomainMatchMetric psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_NONE; - const bool allow_psl_match = - lookup_form && password_manager::ShouldPSLDomainMatchingApply( - password_manager::GetRegistryControlledDomain( - GURL(lookup_form->signon_realm))); for (GList* element = g_list_first(found); element; element = g_list_next(element)) { GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data); GnomeKeyringAttributeList* attrs = data->attributes; std::unique_ptr<PasswordForm> form(FormFromAttributes(attrs)); - if (form) { - if (lookup_form && form->signon_realm != lookup_form->signon_realm) { - if (lookup_form->scheme != PasswordForm::SCHEME_HTML || - form->scheme != PasswordForm::SCHEME_HTML) - continue; // Ignore non-HTML matches. - // This is not an exact match, we try PSL matching and federated match. - if (allow_psl_match && - password_manager::IsPublicSuffixDomainMatch( - form->signon_realm, lookup_form->signon_realm)) { + if (!form) { + LOG(WARNING) << "Could not initialize PasswordForm from attributes!"; + continue; + } + + if (lookup_form) { + switch (GetMatchResult(*form, *lookup_form)) { + case MatchResult::NO_MATCH: + continue; + case MatchResult::EXACT_MATCH: + break; + case MatchResult::PSL_MATCH: psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_FOUND; form->is_public_suffix_match = true; - } else if (!form->federation_origin.unique() && - password_manager::IsFederatedMatch(form->signon_realm, - lookup_form->origin)) { - } else { - continue; - } + break; + case MatchResult::FEDERATED_MATCH: + break; + case MatchResult::FEDERATED_PSL_MATCH: + psl_domain_match_metric = + password_manager::PSL_DOMAIN_MATCH_FOUND_FEDERATED; + form->is_public_suffix_match = true; + break; } - if (data->secret) { - form->password_value = UTF8ToUTF16(data->secret); - } else { - LOG(WARNING) << "Unable to access password from list element!"; - } - forms.push_back(std::move(form)); - } else { - LOG(WARNING) << "Could not initialize PasswordForm from attributes!"; } + + if (data->secret) { + form->password_value = UTF8ToUTF16(data->secret); + } else { + LOG(WARNING) << "Unable to access password from list element!"; + } + forms.push_back(std::move(form)); } if (lookup_form) { + const bool allow_psl_match = password_manager::ShouldPSLDomainMatchingApply( + password_manager::GetRegistryControlledDomain( + GURL(lookup_form->signon_realm))); UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering", allow_psl_match ? psl_domain_match_metric
diff --git a/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc b/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc index e62dc9b..7080a52 100644 --- a/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc +++ b/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc
@@ -894,6 +894,22 @@ PasswordForm::SCHEME_HTML, nullptr)); } +TEST_F(NativeBackendGnomeTest, FetchPSLMatchedFederatedCredentialOnHTTPS) { + other_auth_.signon_realm = "federation://www.sub.example.com/google.com"; + other_auth_.federation_origin = url::Origin(GURL("https://google.com/")); + EXPECT_TRUE(CheckCredentialAvailability(other_auth_, + GURL("https://www.example.com/"), + PasswordForm::SCHEME_HTML, nullptr)); +} + +TEST_F(NativeBackendGnomeTest, DontFetchPSLMatchedFederatedCredentialOnHTTP) { + other_auth_.signon_realm = "federation://www.sub.example.com/google.com"; + other_auth_.federation_origin = url::Origin(GURL("https://google.com/")); + EXPECT_FALSE(CheckCredentialAvailability(other_auth_, + GURL("http://www.example.com/"), + PasswordForm::SCHEME_HTML, nullptr)); +} + TEST_F(NativeBackendGnomeTest, BasicUpdateLogin) { NativeBackendGnome backend(42); backend.Init();
diff --git a/chrome/browser/password_manager/native_backend_libsecret.cc b/chrome/browser/password_manager/native_backend_libsecret.cc index dd21a65..1a6a99ab 100644 --- a/chrome/browser/password_manager/native_backend_libsecret.cc +++ b/chrome/browser/password_manager/native_backend_libsecret.cc
@@ -27,6 +27,7 @@ using autofill::PasswordForm; using base::UTF8ToUTF16; using base::UTF16ToUTF8; +using password_manager::MatchResult; using password_manager::PasswordStore; namespace { @@ -516,10 +517,6 @@ password_manager::PSLDomainMatchMetric psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_NONE; GError* error = nullptr; - const bool allow_psl_match = - lookup_form && password_manager::ShouldPSLDomainMatchingApply( - password_manager::GetRegistryControlledDomain( - GURL(lookup_form->signon_realm))); for (GList* element = g_list_first(found); element != nullptr; element = g_list_next(element)) { SecretItem* secretItem = static_cast<SecretItem*>(element->data); @@ -533,40 +530,47 @@ GHashTable* attrs = LibsecretLoader::secret_item_get_attributes(secretItem); std::unique_ptr<PasswordForm> form(FormOutOfAttributes(attrs)); g_hash_table_unref(attrs); - if (form) { - if (lookup_form && form->signon_realm != lookup_form->signon_realm) { - if (lookup_form->scheme != PasswordForm::SCHEME_HTML || - form->scheme != PasswordForm::SCHEME_HTML) + if (!form) { + VLOG(1) << "Could not initialize PasswordForm from attributes!"; + continue; + } + + if (lookup_form) { + switch (GetMatchResult(*form, *lookup_form)) { + case MatchResult::NO_MATCH: continue; - // This is not an exact match, we try PSL matching and federated match. - if (allow_psl_match && - password_manager::IsPublicSuffixDomainMatch( - form->signon_realm, lookup_form->signon_realm)) { + case MatchResult::EXACT_MATCH: + break; + case MatchResult::PSL_MATCH: psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_FOUND; form->is_public_suffix_match = true; - } else if (!form->federation_origin.unique() && - password_manager::IsFederatedMatch(form->signon_realm, - lookup_form->origin)) { - } else { - continue; - } + break; + case MatchResult::FEDERATED_MATCH: + break; + case MatchResult::FEDERATED_PSL_MATCH: + psl_domain_match_metric = + password_manager::PSL_DOMAIN_MATCH_FOUND_FEDERATED; + form->is_public_suffix_match = true; + break; } - SecretValue* secretValue = - LibsecretLoader::secret_item_get_secret(secretItem); - if (secretValue) { - form->password_value = - UTF8ToUTF16(LibsecretLoader::secret_value_get_text(secretValue)); - LibsecretLoader::secret_value_unref(secretValue); - } else { - LOG(WARNING) << "Unable to access password from list element!"; - } - forms.push_back(std::move(form)); - } else { - VLOG(1) << "Could not initialize PasswordForm from attributes!"; } + + SecretValue* secretValue = + LibsecretLoader::secret_item_get_secret(secretItem); + if (secretValue) { + form->password_value = + UTF8ToUTF16(LibsecretLoader::secret_value_get_text(secretValue)); + LibsecretLoader::secret_value_unref(secretValue); + } else { + LOG(WARNING) << "Unable to access password from list element!"; + } + forms.push_back(std::move(form)); } if (lookup_form) { + const bool allow_psl_match = password_manager::ShouldPSLDomainMatchingApply( + password_manager::GetRegistryControlledDomain( + GURL(lookup_form->signon_realm))); UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering", allow_psl_match ? psl_domain_match_metric
diff --git a/chrome/browser/password_manager/native_backend_libsecret_unittest.cc b/chrome/browser/password_manager/native_backend_libsecret_unittest.cc index 6572194..5c3515e 100644 --- a/chrome/browser/password_manager/native_backend_libsecret_unittest.cc +++ b/chrome/browser/password_manager/native_backend_libsecret_unittest.cc
@@ -701,6 +701,23 @@ PasswordForm::SCHEME_HTML, nullptr)); } +TEST_F(NativeBackendLibsecretTest, FetchPSLMatchedFederatedCredentialOnHTTPS) { + other_auth_.signon_realm = "federation://www.example.com/google.com"; + other_auth_.federation_origin = url::Origin(GURL("https://google.com/")); + EXPECT_TRUE(CheckCredentialAvailability(other_auth_, + GURL("https://www.example.com/"), + PasswordForm::SCHEME_HTML, nullptr)); +} + +TEST_F(NativeBackendLibsecretTest, + DontFetchPSLMatchedFederatedCredentialOnHTTP) { + other_auth_.signon_realm = "federation://www.example.com/google.com"; + other_auth_.federation_origin = url::Origin(GURL("https://google.com/")); + EXPECT_TRUE(CheckCredentialAvailability(other_auth_, + GURL("http://www.example.com/"), + PasswordForm::SCHEME_HTML, nullptr)); +} + TEST_F(NativeBackendLibsecretTest, BasicUpdateLogin) { NativeBackendLibsecret backend(42);
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.cc b/chrome/browser/predictors/resource_prefetch_predictor.cc index 2da784f0..67dd25a 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor.cc +++ b/chrome/browser/predictors/resource_prefetch_predictor.cc
@@ -736,11 +736,6 @@ url_redirect_table_cache_ = std::move(url_redirect_data_map); host_redirect_table_cache_ = std::move(host_redirect_data_map); - UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.UrlTableMainFrameUrlCount", - url_table_cache_->size()); - UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.HostTableHostCount", - host_table_cache_->size()); - ConnectToHistoryService(); }
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_tables.cc b/chrome/browser/predictors/resource_prefetch_predictor_tables.cc index f5be8ec..7d5515e 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor_tables.cc +++ b/chrome/browser/predictors/resource_prefetch_predictor_tables.cc
@@ -451,14 +451,14 @@ base::StringPrintf("SELECT count(*) FROM %s", kUrlResourceTableName) .c_str())); if (statement.Step()) - UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.UrlTableRowCount", + UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.UrlTableRowCount2", statement.ColumnInt(0)); statement.Assign(DB()->GetUniqueStatement( base::StringPrintf("SELECT count(*) FROM %s", kHostResourceTableName) .c_str())); if (statement.Step()) - UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.HostTableRowCount", + UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.HostTableRowCount2", statement.ColumnInt(0)); }
diff --git a/chrome/browser/resources/chromeos/login/gaia_input.css b/chrome/browser/resources/chromeos/login/gaia_input.css index de31266..af1bfc4 100644 --- a/chrome/browser/resources/chromeos/login/gaia_input.css +++ b/chrome/browser/resources/chromeos/login/gaia_input.css
@@ -14,7 +14,6 @@ }; --paper-input-container-input-color: rgba(0, 0, 0, 0.87); --paper-input-container-label: { - color: rgba(0, 0, 0, 0.54); font-size: 15px; }; } @@ -23,6 +22,10 @@ padding: 0; } +paper-input-container > label > span { + color: rgba(0, 0, 0, 0.54); +} + :host-context(html[dir=rtl]) input { flex-direction: row-reverse; }
diff --git a/chrome/browser/resources/chromeos/login/offline_ad_login.js b/chrome/browser/resources/chromeos/login/offline_ad_login.js index f722ecf..6c202bc 100644 --- a/chrome/browser/resources/chromeos/login/offline_ad_login.js +++ b/chrome/browser/resources/chromeos/login/offline_ad_login.js
@@ -124,6 +124,8 @@ onSubmit_: function() { if (this.showMachineInput && !this.$.machineNameInput.checkValidity()) return; + if (!this.$.userInput.checkValidity()) + return; if (!this.$.passwordInput.checkValidity()) return; var user = /** @type {string} */ (this.$.userInput.value);
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc index 3f20b35ec..28fbabb7 100644 --- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc +++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
@@ -10,15 +10,15 @@ #include "base/macros.h" #include "base/strings/stringprintf.h" #include "base/values.h" +#include "chrome/browser/supervised_user/child_accounts/kids_management_api.h" #include "components/data_use_measurement/core/data_use_user_data.h" #include "net/base/load_flags.h" #include "net/http/http_status_code.h" #include "net/url_request/url_request_status.h" #include "url/gurl.h" -const char kFamilyApiUrl[] = "https://www.googleapis.com/kidsmanagement/v1/"; -const char kGetFamilyProfileApiSuffix[] = "families/mine?alt=json"; -const char kGetFamilyMembersApiSuffix[] = "families/mine/members?alt=json"; +const char kGetFamilyProfileApiPath[] = "families/mine?alt=json"; +const char kGetFamilyMembersApiPath[] = "families/mine/members?alt=json"; const char kScope[] = "https://www.googleapis.com/auth/kid.family.readonly"; const char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s"; const int kNumRetries = 1; @@ -89,7 +89,6 @@ account_id_(account_id), token_service_(token_service), request_context_(request_context), - request_type_(net::URLFetcher::GET), access_token_expired_(false) { } @@ -118,14 +117,12 @@ } void FamilyInfoFetcher::StartGetFamilyProfile() { - request_suffix_ = kGetFamilyProfileApiSuffix; - request_type_ = net::URLFetcher::GET; + request_path_ = kGetFamilyProfileApiPath; StartFetching(); } void FamilyInfoFetcher::StartGetFamilyMembers() { - request_suffix_ = kGetFamilyMembersApiSuffix; - request_type_ = net::URLFetcher::GET; + request_path_ = kGetFamilyMembersApiPath; StartFetching(); } @@ -141,8 +138,8 @@ void FamilyInfoFetcher::StartFetchingAccessToken() { OAuth2TokenService::ScopeSet scopes; scopes.insert(kScope); - access_token_request_ = token_service_->StartRequest( - account_id_, scopes, this); + access_token_request_ = + token_service_->StartRequest(account_id_, scopes, this); } void FamilyInfoFetcher::OnRefreshTokenAvailable( @@ -172,9 +169,9 @@ DCHECK_EQ(access_token_request_.get(), request); access_token_ = access_token; - GURL url(kFamilyApiUrl + request_suffix_); + GURL url = kids_management_api::GetURL(request_path_); const int id = 0; - url_fetcher_ = net::URLFetcher::Create(id, url, request_type_, this); + url_fetcher_ = net::URLFetcher::Create(id, url, net::URLFetcher::GET, this); data_use_measurement::DataUseUserData::AttachToFetcher( url_fetcher_.get(), @@ -226,9 +223,9 @@ std::string response_body; source->GetResponseAsString(&response_body); - if (request_suffix_ == kGetFamilyProfileApiSuffix) { + if (request_path_ == kGetFamilyProfileApiPath) { FamilyProfileFetched(response_body); - } else if (request_suffix_ == kGetFamilyMembersApiSuffix) { + } else if (request_path_ == kGetFamilyMembersApiPath) { FamilyMembersFetched(response_body); } else { NOTREACHED();
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h index a2e61c0..2adb0b06 100644 --- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h +++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
@@ -25,6 +25,9 @@ class URLRequestContextGetter; } +// Fetches information about the family of the signed-in user. It can get +// information about the family itself (e.g. a name), as well as a list of +// family members and their properties. class FamilyInfoFetcher : public OAuth2TokenService::Observer, public OAuth2TokenService::Consumer, public net::URLFetcherDelegate { @@ -34,6 +37,8 @@ NETWORK_ERROR, // Network failure. SERVICE_ERROR, // Service returned an error or malformed reply. }; + // Note: If you add or update an entry, also update |kFamilyMemberRoleStrings| + // in the .cc file. enum FamilyMemberRole { HEAD_OF_HOUSEHOLD = 0, PARENT, @@ -74,6 +79,8 @@ virtual void OnFailure(ErrorCode error) {} }; + // Instantiates a fetcher, but doesn't start a fetch - use the StartGet* + // methods below. |consumer| must outlive us. FamilyInfoFetcher(Consumer* consumer, const std::string& account_id, OAuth2TokenService* token_service, @@ -84,6 +91,8 @@ static std::string RoleToString(FamilyMemberRole role); static bool StringToRole(const std::string& str, FamilyMemberRole* role); + // Start a fetch for the family profile or members. + // Note: Only one fetch is supported at a time. void StartGetFamilyProfile(); void StartGetFamilyMembers(); @@ -119,8 +128,7 @@ OAuth2TokenService* token_service_; net::URLRequestContextGetter* request_context_; - std::string request_suffix_; - net::URLFetcher::RequestType request_type_; + std::string request_path_; std::unique_ptr<OAuth2TokenService::Request> access_token_request_; std::string access_token_; bool access_token_expired_; @@ -130,4 +138,3 @@ }; #endif // CHROME_BROWSER_SUPERVISED_USER_CHILD_ACCOUNTS_FAMILY_INFO_FETCHER_H_ -
diff --git a/chrome/browser/supervised_user/child_accounts/kids_management_api.cc b/chrome/browser/supervised_user/child_accounts/kids_management_api.cc new file mode 100644 index 0000000..f22f716 --- /dev/null +++ b/chrome/browser/supervised_user/child_accounts/kids_management_api.cc
@@ -0,0 +1,41 @@ +// Copyright 2017 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 "chrome/browser/supervised_user/child_accounts/kids_management_api.h" + +#include "base/feature_list.h" +#include "components/variations/variations_associated_data.h" +#include "url/gurl.h" + +namespace kids_management_api { + +namespace { + +const char kDefaultBaseURL[] = + "https://kidsmanagement-pa.googleapis.com/kidsmanagement/v1/"; + +// A dummy feature that can be used to specify a variation param that overrides +// the default API URL. +const base::Feature kKidsManagementAPIFeature{ + "KidsManagementAPI", base::FEATURE_DISABLED_BY_DEFAULT}; + +const char kURLParamName[] = "kids_management_api_url"; + +} // namespace + +GURL GetBaseURL() { + // If the parameter isn't set or the feature is disabled, this will return + // the empty string, resulting in an invalid URL. + GURL url(variations::GetVariationParamValueByFeature( + kKidsManagementAPIFeature, kURLParamName)); + if (url.is_valid()) + return url; + return GURL(kDefaultBaseURL); +} + +GURL GetURL(const std::string& path) { + return GetBaseURL().Resolve(path); +} + +} // namespace kids_management_api
diff --git a/chrome/browser/supervised_user/child_accounts/kids_management_api.h b/chrome/browser/supervised_user/child_accounts/kids_management_api.h new file mode 100644 index 0000000..927077b8 --- /dev/null +++ b/chrome/browser/supervised_user/child_accounts/kids_management_api.h
@@ -0,0 +1,20 @@ +// Copyright 2017 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_BROWSER_SUPERVISED_USER_CHILD_ACCOUNTS_KIDS_MANAGEMENT_API_H_ +#define CHROME_BROWSER_SUPERVISED_USER_CHILD_ACCOUNTS_KIDS_MANAGEMENT_API_H_ + +#include <string> + +class GURL; + +namespace kids_management_api { + +GURL GetBaseURL(); + +GURL GetURL(const std::string& path); + +} // namespace kids_management_api + +#endif // CHROME_BROWSER_SUPERVISED_USER_CHILD_ACCOUNTS_KIDS_MANAGEMENT_API_H_
diff --git a/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc b/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc index d28092d..f887bb5 100644 --- a/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc +++ b/chrome/browser/supervised_user/child_accounts/permission_request_creator_apiary.cc
@@ -15,6 +15,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager_factory.h" +#include "chrome/browser/supervised_user/child_accounts/kids_management_api.h" #include "chrome/common/chrome_switches.h" #include "components/data_use_measurement/core/data_use_user_data.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" @@ -30,8 +31,7 @@ using net::URLFetcher; -const char kApiUrl[] = - "https://www.googleapis.com/kidsmanagement/v1/people/me/permissionRequests"; +const char kApiPath[] = "people/me/permissionRequests"; const char kApiScope[] = "https://www.googleapis.com/auth/kid.permission"; const int kNumRetries = 1; @@ -138,9 +138,9 @@ LOG_IF(WARNING, !url.is_valid()) << "Got invalid URL for " << switches::kPermissionRequestApiUrl; return url; - } else { - return GURL(kApiUrl); } + + return kids_management_api::GetURL(kApiPath); } std::string PermissionRequestCreatorApiary::GetApiScope() const {
diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp index a02a49b..8e98eb2a5 100644 --- a/chromeos/chromeos.gyp +++ b/chromeos/chromeos.gyp
@@ -441,6 +441,7 @@ 'dbus/cras_audio_client_unittest.cc', 'dbus/cros_disks_client_unittest.cc', 'dbus/dbus_thread_manager_unittest.cc', + 'dbus/fake_auth_policy_client_unittest.cc', 'dbus/fake_cryptohome_client_unittest.cc', 'dbus/fake_easy_unlock_client_unittest.cc', 'dbus/fake_power_manager_client_unittest.cc',
diff --git a/chromeos/dbus/auth_policy_client.cc b/chromeos/dbus/auth_policy_client.cc index 52d7cc9a..ade93a7 100644 --- a/chromeos/dbus/auth_policy_client.cc +++ b/chromeos/dbus/auth_policy_client.cc
@@ -14,9 +14,12 @@ namespace { -// The first device policy fetch after joining Active Directory can be very slow -// because machine credentials need to propagate through the AD deployment. -const int kRefreshDevicePolicyTimeoutMilliseconds = 90000; +// Policy fetch may take up to 300 seconds. To ensure that a second policy +// fetch queuing after the first one can succeed (e.g. user policy following +// device policy), the D-Bus timeout needs to be at least twice that value. +// JoinADDomain() is an exception since it's always guaranteed to be the first +// call. +constexpr int kSlowDbusTimeoutMilliseconds = 630 * 1000; authpolicy::ErrorType GetErrorFromReader(dbus::MessageReader* reader) { int32_t int_error; @@ -59,7 +62,7 @@ dbus::MessageWriter writer(&method_call); writer.AppendString(user_principal_name); writer.AppendFileDescriptor(password_fd); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kSlowDbusTimeoutMilliseconds, base::Bind(&AuthPolicyClientImpl::HandleAuthCallback, weak_ptr_factory_.GetWeakPtr(), callback)); } @@ -68,7 +71,7 @@ dbus::MethodCall method_call(authpolicy::kAuthPolicyInterface, authpolicy::kAuthPolicyRefreshDevicePolicy); proxy_->CallMethod( - &method_call, kRefreshDevicePolicyTimeoutMilliseconds, + &method_call, kSlowDbusTimeoutMilliseconds, base::Bind(&AuthPolicyClientImpl::HandleRefreshPolicyCallback, weak_ptr_factory_.GetWeakPtr(), callback)); } @@ -81,7 +84,7 @@ dbus::MessageWriter writer(&method_call); writer.AppendString(account_id.GetAccountIdKey()); proxy_->CallMethod( - &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + &method_call, kSlowDbusTimeoutMilliseconds, base::Bind(&AuthPolicyClientImpl::HandleRefreshPolicyCallback, weak_ptr_factory_.GetWeakPtr(), callback)); }
diff --git a/chromeos/dbus/fake_auth_policy_client.cc b/chromeos/dbus/fake_auth_policy_client.cc index f37f1ea5..4f3a35de 100644 --- a/chromeos/dbus/fake_auth_policy_client.cc +++ b/chromeos/dbus/fake_auth_policy_client.cc
@@ -10,6 +10,7 @@ #include "base/location.h" #include "base/md5.h" #include "base/path_service.h" +#include "base/strings/string_split.h" #include "base/task_scheduler/post_task.h" #include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h" #include "chromeos/chromeos_paths.h" @@ -24,6 +25,9 @@ namespace { +const size_t kMaxMachineNameLength = 15; +const char kInvalidMachineNameCharacters[] = "\\/:*?\"<>|"; + // Drop stub policy file of |policy_type| at |policy_path| containing // |serialized_payload|. bool WritePolicyFile(const base::FilePath& policy_path, @@ -65,6 +69,25 @@ const std::string& user_principal_name, int password_fd, const JoinCallback& callback) { + if (machine_name.size() > kMaxMachineNameLength) { + callback.Run(authpolicy::ERROR_MACHINE_NAME_TOO_LONG); + return; + } + + if (machine_name.empty() || + machine_name.find_first_of(kInvalidMachineNameCharacters) != + std::string::npos) { + callback.Run(authpolicy::ERROR_BAD_MACHINE_NAME); + return; + } + + std::vector<std::string> parts = base::SplitString( + user_principal_name, "@", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + if (parts.size() != 2 || parts[0].empty() || parts[1].empty()) { + callback.Run(authpolicy::ERROR_PARSE_UPN_FAILED); + return; + } + callback.Run(authpolicy::ERROR_NONE); }
diff --git a/chromeos/dbus/fake_auth_policy_client_unittest.cc b/chromeos/dbus/fake_auth_policy_client_unittest.cc new file mode 100644 index 0000000..a8b6b79 --- /dev/null +++ b/chromeos/dbus/fake_auth_policy_client_unittest.cc
@@ -0,0 +1,61 @@ +// Copyright 2017 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 "chromeos/dbus/fake_auth_policy_client.h" + +#include "base/bind.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { +namespace { + +const char kCorrectMachineName[] = "machine_name"; +const char kCorrectUserName[] = "user@realm.com"; + +void JoinAdCallback(const std::string& machine_name, + const std::string& user_principal_name, + authpolicy::ErrorType expected, + authpolicy::ErrorType actual) { + EXPECT_EQ(expected, actual) << "with machine name: " << machine_name + << ", and user name: " << user_principal_name; +} + +void TestDomainJoin(const std::string& machine_name, + const std::string& user_principal_name, + authpolicy::ErrorType expected) { + FakeAuthPolicyClient client; + client.JoinAdDomain( + machine_name, user_principal_name, /* password_fd */ -1, + base::Bind(&JoinAdCallback, machine_name, user_principal_name, expected)); +} + +} // namespace + +// Tests parsing machine name. +TEST(FakeAuthPolicyClientTest, JoinAdDomain_ParseMachineName) { + TestDomainJoin("correct_length1", kCorrectUserName, authpolicy::ERROR_NONE); + TestDomainJoin("", kCorrectUserName, authpolicy::ERROR_BAD_MACHINE_NAME); + TestDomainJoin("too_long_machine_name ", kCorrectUserName, + authpolicy::ERROR_MACHINE_NAME_TOO_LONG); + TestDomainJoin("invalid:name", kCorrectUserName, + authpolicy::ERROR_BAD_MACHINE_NAME); + TestDomainJoin(">nvalidname", kCorrectUserName, + authpolicy::ERROR_BAD_MACHINE_NAME); +} + +// Tests parsing user name. +TEST(FakeAuthPolicyClientTest, JoinAdDomain_ParseUPN) { + TestDomainJoin(kCorrectMachineName, "user@realm.com", authpolicy::ERROR_NONE); + TestDomainJoin(kCorrectMachineName, "user", + authpolicy::ERROR_PARSE_UPN_FAILED); + TestDomainJoin(kCorrectMachineName, "", authpolicy::ERROR_PARSE_UPN_FAILED); + TestDomainJoin(kCorrectMachineName, "user@", + authpolicy::ERROR_PARSE_UPN_FAILED); + TestDomainJoin(kCorrectMachineName, "@realm", + authpolicy::ERROR_PARSE_UPN_FAILED); + TestDomainJoin(kCorrectMachineName, "user@realm@com", + authpolicy::ERROR_PARSE_UPN_FAILED); +} + +} // namespace chromeos
diff --git a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc index 9ab832f..9e3e072 100644 --- a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc +++ b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
@@ -37,6 +37,7 @@ using testing::Pointee; using testing::Return; using testing::UnorderedElementsAre; +using testing::WithArg; namespace password_manager { @@ -144,7 +145,7 @@ } // Small helper that wraps passed in forms in unique ptrs. -std::vector<std::unique_ptr<PasswordForm>> make_results( +std::vector<std::unique_ptr<PasswordForm>> MakeResults( const std::vector<PasswordForm>& forms) { std::vector<std::unique_ptr<PasswordForm>> results; results.reserve(forms.size()); @@ -170,9 +171,8 @@ MOCK_METHOD0(ProcessMigratedFormsDummy, void()); }; -ACTION(InvokeMigratorGetResults) { - static_cast<HttpPasswordMigrator*>(arg0)->OnGetPasswordStoreResults( - std::vector<std::unique_ptr<PasswordForm>>()); +ACTION_P(GetAndAssignWeakPtr, ptr) { + *ptr = arg0->GetWeakPtr(); } } // namespace @@ -412,30 +412,40 @@ GURL::Replacements http_rep; http_rep.SetSchemeStr(url::kHttpScheme); const GURL http_origin = form_digest_.origin.ReplaceComponents(http_rep); - const PasswordStore::FormDigest http_form_digest( + form_digest_ = PasswordStore::FormDigest( PasswordForm::SCHEME_HTML, http_origin.GetOrigin().spec(), http_origin); // A new form fetcher is created to be able to set the form digest and // migration flag. - const auto http_form_fetcher = base::MakeUnique<MockFormFetcherImpl>( - http_form_digest, &client_, /* should_migrate_http_passwords */ true); + form_fetcher_ = base::MakeUnique<MockFormFetcherImpl>( + form_digest_, &client_, /* should_migrate_http_passwords */ true); + EXPECT_CALL(consumer_, ProcessMatches(IsEmpty(), 0u)); + form_fetcher_->AddConsumer(&consumer_); std::vector<PasswordForm> empty_forms; const PasswordForm http_form = CreateHTTPNonFederated(); + const PasswordForm federated_form = CreateFederated(); - // Tests that there is no attempt to migrate credentials on HTTP origins. - // FormFetcherImplTest::Fetch() can't be used here due to different - // expectations. The repeated calls to MockFormFetcherImpl::Fetch() are - // necessary to set the correct state. - EXPECT_CALL(*mock_store_, GetLogins(_, _)); - http_form_fetcher->Fetch(); + Fetch(); EXPECT_CALL(*mock_store_, GetLogins(_, _)).Times(0); - http_form_fetcher->OnGetPasswordStoreResults(make_results(empty_forms)); + EXPECT_CALL(*mock_store_, AddLogin(_)).Times(0); + EXPECT_CALL(consumer_, ProcessMatches(IsEmpty(), 0u)); + form_fetcher_->OnGetPasswordStoreResults(MakeResults(empty_forms)); + EXPECT_THAT(form_fetcher_->GetFederatedMatches(), IsEmpty()); - EXPECT_CALL(*mock_store_, GetLogins(_, _)); - http_form_fetcher->Fetch(); - EXPECT_CALL(*mock_store_, GetLogins(_, _)).Times(0); - http_form_fetcher->OnGetPasswordStoreResults(make_results({http_form})); + Fetch(); + EXPECT_CALL(consumer_, + ProcessMatches(UnorderedElementsAre(Pointee(http_form)), 0u)); + form_fetcher_->OnGetPasswordStoreResults(MakeResults({http_form})); + EXPECT_THAT(form_fetcher_->GetFederatedMatches(), IsEmpty()); + + Fetch(); + EXPECT_CALL(consumer_, + ProcessMatches(UnorderedElementsAre(Pointee(http_form)), 0u)); + form_fetcher_->OnGetPasswordStoreResults( + MakeResults({http_form, federated_form})); + EXPECT_THAT(form_fetcher_->GetFederatedMatches(), + UnorderedElementsAre(Pointee(federated_form))); } // Test that ensures HTTP passwords are only migrated on HTTPS sites when no @@ -444,28 +454,73 @@ GURL::Replacements https_rep; https_rep.SetSchemeStr(url::kHttpsScheme); const GURL https_origin = form_digest_.origin.ReplaceComponents(https_rep); - const PasswordStore::FormDigest https_form_digest( + form_digest_ = PasswordStore::FormDigest( PasswordForm::SCHEME_HTML, https_origin.GetOrigin().spec(), https_origin); // A new form fetcher is created to be able to set the form digest and // migration flag. - const auto https_form_fetcher = base::MakeUnique<MockFormFetcherImpl>( - https_form_digest, &client_, /* should_migrate_http_passwords */ true); + form_fetcher_ = base::MakeUnique<MockFormFetcherImpl>( + form_digest_, &client_, /* should_migrate_http_passwords */ true); + EXPECT_CALL(consumer_, ProcessMatches(IsEmpty(), 0u)); + form_fetcher_->AddConsumer(&consumer_); + + PasswordForm https_form = CreateNonFederated(); + + // Create HTTP form for the same orgin (except scheme), which will be passed + // to the migrator. + GURL::Replacements http_rep; + http_rep.SetSchemeStr(url::kHttpScheme); + PasswordForm http_form = https_form; + http_form.origin = https_form.origin.ReplaceComponents(http_rep); + http_form.signon_realm = http_form.origin.GetOrigin().spec(); std::vector<PasswordForm> empty_forms; - const PasswordForm https_form = CreateNonFederated(); // Tests that there is only an attempt to migrate credentials on HTTPS origins // when no other credentials are available. - // FormFetcherImplTest::Fetch() can't be used here due to different - // expectations. The repeated calls to MockFormFetcherImpl::Fetch() are - // necessary to set the correct state. - EXPECT_CALL(*mock_store_, GetLogins(_, _)); - https_form_fetcher->Fetch(); - EXPECT_CALL(*mock_store_, GetLogins(_, _)) - .WillOnce(testing::WithArg<1>(InvokeMigratorGetResults())); - EXPECT_CALL(*https_form_fetcher, ProcessMigratedFormsDummy()); - https_form_fetcher->OnGetPasswordStoreResults(make_results(empty_forms)); + const GURL form_digest_http_origin = + form_digest_.origin.ReplaceComponents(http_rep); + PasswordStore::FormDigest http_form_digest( + PasswordForm::SCHEME_HTML, form_digest_http_origin.GetOrigin().spec(), + form_digest_http_origin); + Fetch(); + base::WeakPtr<PasswordStoreConsumer> migrator_ptr; + EXPECT_CALL(*mock_store_, GetLogins(http_form_digest, _)) + .WillOnce(WithArg<1>(GetAndAssignWeakPtr(&migrator_ptr))); + form_fetcher_->OnGetPasswordStoreResults(MakeResults(empty_forms)); + ASSERT_TRUE(migrator_ptr); + + // Setting action equal to origin is necessary in order to mirror the behavior + // in HttpPasswordMigrator. + https_form.action = https_form.origin; + + // Now perform the actual migration. + EXPECT_CALL(*mock_store_, AddLogin(https_form)); + EXPECT_CALL(*static_cast<MockFormFetcherImpl*>(form_fetcher_.get()), + ProcessMigratedFormsDummy()); + EXPECT_CALL(consumer_, + ProcessMatches(UnorderedElementsAre(Pointee(https_form)), 0u)); + static_cast<HttpPasswordMigrator*>(migrator_ptr.get()) + ->OnGetPasswordStoreResults(MakeResults({http_form})); + EXPECT_THAT(form_fetcher_->GetFederatedMatches(), IsEmpty()); + + // No migration should happen when results are present. + Fetch(); + EXPECT_CALL(*mock_store_, GetLogins(_, _)).Times(0); + EXPECT_CALL(*mock_store_, AddLogin(_)).Times(0); + EXPECT_CALL(consumer_, + ProcessMatches(UnorderedElementsAre(Pointee(https_form)), 0u)); + form_fetcher_->OnGetPasswordStoreResults(MakeResults({https_form})); + EXPECT_THAT(form_fetcher_->GetFederatedMatches(), IsEmpty()); + + const PasswordForm federated_form = CreateFederated(); + Fetch(); + EXPECT_CALL(consumer_, + ProcessMatches(UnorderedElementsAre(Pointee(https_form)), 0u)); + form_fetcher_->OnGetPasswordStoreResults( + MakeResults({https_form, federated_form})); + EXPECT_THAT(form_fetcher_->GetFederatedMatches(), + UnorderedElementsAre(Pointee(federated_form))); } } // namespace password_manager
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc index 586ba07..ff7d4da 100644 --- a/components/password_manager/core/browser/login_database.cc +++ b/components/password_manager/core/browser/login_database.cc
@@ -1217,26 +1217,26 @@ if (result == ENCRYPTION_RESULT_ITEM_FAILURE) continue; DCHECK_EQ(ENCRYPTION_RESULT_SUCCESS, result); - if (matched_form && matched_form->signon_realm != new_form->signon_realm) { - if (new_form->scheme != PasswordForm::SCHEME_HTML) - continue; // Ignore non-HTML matches. - if (IsPublicSuffixDomainMatch(new_form->signon_realm, - matched_form->signon_realm)) { - psl_domain_match_metric = PSL_DOMAIN_MATCH_FOUND; - new_form->is_public_suffix_match = true; - } else if (!new_form->federation_origin.unique() && - IsFederatedMatch(new_form->signon_realm, - matched_form->origin)) { - } else if (!new_form->federation_origin.unique() && - IsFederatedPSLMatch(new_form->signon_realm, - matched_form->origin)) { - psl_domain_match_metric = PSL_DOMAIN_MATCH_FOUND_FEDERATED; - new_form->is_public_suffix_match = true; - } else { - continue; + if (matched_form) { + switch (GetMatchResult(*new_form, *matched_form)) { + case MatchResult::NO_MATCH: + continue; + case MatchResult::EXACT_MATCH: + break; + case MatchResult::PSL_MATCH: + psl_domain_match_metric = PSL_DOMAIN_MATCH_FOUND; + new_form->is_public_suffix_match = true; + break; + case MatchResult::FEDERATED_MATCH: + break; + case MatchResult::FEDERATED_PSL_MATCH: + psl_domain_match_metric = PSL_DOMAIN_MATCH_FOUND_FEDERATED; + new_form->is_public_suffix_match = true; + break; } } + forms->push_back(std::move(new_form)); }
diff --git a/components/password_manager/core/browser/psl_matching_helper.cc b/components/password_manager/core/browser/psl_matching_helper.cc index 8790e8a..02bccbe4 100644 --- a/components/password_manager/core/browser/psl_matching_helper.cc +++ b/components/password_manager/core/browser/psl_matching_helper.cc
@@ -5,6 +5,7 @@ #include "components/password_manager/core/browser/psl_matching_helper.h" #include <memory> +#include <ostream> #include "base/logging.h" #include "base/strings/string_util.h" @@ -17,6 +18,53 @@ namespace password_manager { +std::ostream& operator<<(std::ostream& out, MatchResult result) { + switch (result) { + case MatchResult::NO_MATCH: + return out << "No Match"; + case MatchResult::EXACT_MATCH: + return out << "Exact Match"; + case MatchResult::PSL_MATCH: + return out << "PSL Match"; + case MatchResult::FEDERATED_MATCH: + return out << "Federated Match"; + case MatchResult::FEDERATED_PSL_MATCH: + return out << "Federated PSL Match"; + } + // This should never be reached, it is simply here to suppress compiler + // warnings. + return out; +} + +MatchResult GetMatchResult(const PasswordForm& form, + const PasswordStore::FormDigest& form_digest) { + if (form.signon_realm == form_digest.signon_realm) + return MatchResult::EXACT_MATCH; + + // PSL and federated matches only apply to HTML forms. + if (form_digest.scheme != PasswordForm::SCHEME_HTML || + form.scheme != PasswordForm::SCHEME_HTML) + return MatchResult::NO_MATCH; + + const bool allow_psl_match = ShouldPSLDomainMatchingApply( + GetRegistryControlledDomain(GURL(form_digest.signon_realm))); + const bool allow_federated_match = !form.federation_origin.unique(); + + if (allow_psl_match && + IsPublicSuffixDomainMatch(form.signon_realm, form_digest.signon_realm)) + return MatchResult::PSL_MATCH; + + if (allow_federated_match && + IsFederatedMatch(form.signon_realm, form_digest.origin)) + return MatchResult::FEDERATED_MATCH; + + if (allow_psl_match && allow_federated_match && + IsFederatedPSLMatch(form.signon_realm, form_digest.origin)) + return MatchResult::FEDERATED_PSL_MATCH; + + return MatchResult::NO_MATCH; +} + bool ShouldPSLDomainMatchingApply( const std::string& registry_controlled_domain) { return !registry_controlled_domain.empty() &&
diff --git a/components/password_manager/core/browser/psl_matching_helper.h b/components/password_manager/core/browser/psl_matching_helper.h index 56b2ac4..d5839c5 100644 --- a/components/password_manager/core/browser/psl_matching_helper.h +++ b/components/password_manager/core/browser/psl_matching_helper.h
@@ -5,8 +5,10 @@ #ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PSL_MATCHING_HELPER_H_ #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PSL_MATCHING_HELPER_H_ +#include <iosfwd> #include <string> +#include "components/password_manager/core/browser/password_store.h" class GURL; @@ -23,6 +25,21 @@ PSL_DOMAIN_MATCH_COUNT }; +enum class MatchResult { + NO_MATCH, + EXACT_MATCH, + PSL_MATCH, + FEDERATED_MATCH, + FEDERATED_PSL_MATCH, +}; + +// For testing. +std::ostream& operator<<(std::ostream& out, MatchResult result); + +// Returns what type of match applies to |form| and |form_digest|. +MatchResult GetMatchResult(const autofill::PasswordForm& form, + const PasswordStore::FormDigest& form_digest); + // Using the public suffix list for matching the origin is only needed for // websites that do not have a single hostname for entering credentials. It // would be better for their users if they did, but until then we help them find
diff --git a/components/password_manager/core/browser/psl_matching_helper_unittest.cc b/components/password_manager/core/browser/psl_matching_helper_unittest.cc index 68381d2c..b98dddc 100644 --- a/components/password_manager/core/browser/psl_matching_helper_unittest.cc +++ b/components/password_manager/core/browser/psl_matching_helper_unittest.cc
@@ -14,6 +14,154 @@ namespace { +TEST(PSLMatchingUtilsTest, GetMatchResult) { + struct TestData { + const char* form_signon_realm; + const char* form_federation_origin; + autofill::PasswordForm::Scheme digest_scheme; + const char* digest_signon_realm; + const char* digest_origin; + MatchResult match_result; + }; + + const TestData cases[] = { + // Test Exact Matches. + {"http://facebook.com/", "", autofill::PasswordForm::SCHEME_HTML, + "http://facebook.com/", "http://facebook.com", MatchResult::EXACT_MATCH}, + + {"http://m.facebook.com/", "", autofill::PasswordForm::SCHEME_HTML, + "http://m.facebook.com/", "http://m.facebook.com", + MatchResult::EXACT_MATCH}, + + // Scheme mismatch. + {"http://www.example.com/", "", autofill::PasswordForm::SCHEME_HTML, + "https://www.example.com/", "https://www.example.com", + MatchResult::NO_MATCH}, + + // Host mismatch. + {"http://www.example.com/", "", autofill::PasswordForm::SCHEME_HTML, + "http://wwwexample.com/", "http://wwwexample.com", + MatchResult::NO_MATCH}, + + {"http://www.example1.com/", "", autofill::PasswordForm::SCHEME_HTML, + "http://www.example2.com/", "http://www.example2.com", + MatchResult::NO_MATCH}, + + // Port mismatch. + {"http://www.example.com:123/", "", autofill::PasswordForm::SCHEME_HTML, + "http://www.example.com/", "http://www.example.com", + MatchResult::NO_MATCH}, + + // TLD mismatch. + {"http://www.example.org/", "", autofill::PasswordForm::SCHEME_HTML, + "http://www.example.com/", "http://www.example.com", + MatchResult::NO_MATCH}, + + // URLs without registry controlled domains should not match. + {"http://localhost/", "", autofill::PasswordForm::SCHEME_HTML, + "http://127.0.0.1/", "http://127.0.0.1", MatchResult::NO_MATCH}, + + // Invalid URLs don't match. + {"http://www.example.com/", "", autofill::PasswordForm::SCHEME_HTML, + "http://", "", MatchResult::NO_MATCH}, + + {"", "", autofill::PasswordForm::SCHEME_HTML, "http://www.example.com/", + "http://www.example.com", MatchResult::NO_MATCH}, + + {"http://www.example.com", "", autofill::PasswordForm::SCHEME_HTML, + "bad url", "", MatchResult::NO_MATCH}, + + // Test PSL Matches. + {"http://facebook.com/", "", autofill::PasswordForm::SCHEME_HTML, + "http://m.facebook.com/", "http://m.facebook.com", + MatchResult::PSL_MATCH}, + + {"https://facebook.com/", "", autofill::PasswordForm::SCHEME_HTML, + "https://m.facebook.com/", "https://m.facebook.com", + MatchResult::PSL_MATCH}, + + {"https://www.facebook.com/", "", autofill::PasswordForm::SCHEME_HTML, + "https://m.facebook.com/", "https://m.facebook.com", + MatchResult::PSL_MATCH}, + + // Don't apply PSL matching to Google domains. + {"https://google.com/", "", autofill::PasswordForm::SCHEME_HTML, + "https://maps.google.com/", "https://maps.google.com", + MatchResult::NO_MATCH}, + + {"https://google.com/", "", autofill::PasswordForm::SCHEME_HTML, + "https://maps.google.com/", "https://maps.google.com", + MatchResult::NO_MATCH}, + + // Test Federated Matches. + {"federation://example.com/google.com", "https://google.com", + autofill::PasswordForm::SCHEME_HTML, "https://example.com/", + "https://example.com", MatchResult::FEDERATED_MATCH}, + + // Empty federation providers don't match. + {"federation://example.com/", "", autofill::PasswordForm::SCHEME_HTML, + "https://example.com/", "https://example.com", MatchResult::NO_MATCH}, + + // Invalid origins don't match. + {"federation://example.com/google.com", "https://google.com", + autofill::PasswordForm::SCHEME_HTML, "https://example.com", + "example.com", MatchResult::NO_MATCH}, + + {"federation://example.com/google.com", "https://google.com", + autofill::PasswordForm::SCHEME_HTML, "https://example.com", "example", + MatchResult::NO_MATCH}, + + // Test Federated PSL Matches. + {"federation://sub.example.com/google.com", "https://google.com", + autofill::PasswordForm::SCHEME_HTML, "https://sub.example.com/", + "https://sub.example.com", MatchResult::FEDERATED_MATCH}, + + {"federation://sub1.example.com/google.com", "https://google.com", + autofill::PasswordForm::SCHEME_HTML, "https://sub2.example.com/", + "https://sub2.example.com", MatchResult::FEDERATED_PSL_MATCH}, + + {"federation://example.com/google.com", "https://google.com", + autofill::PasswordForm::SCHEME_HTML, "https://sub.example.com/", + "https://sub.example.com", MatchResult::FEDERATED_PSL_MATCH}, + + // Federated PSL matches do not apply to HTTP. + {"federation://sub1.example.com/google.com", "https://google.com", + autofill::PasswordForm::SCHEME_HTML, "http://sub2.example.com/", + "http://sub2.example.com", MatchResult::NO_MATCH}, + + {"federation://example.com/google.com", "https://google.com", + autofill::PasswordForm::SCHEME_HTML, "http://sub.example.com/", + "http://sub.example.com", MatchResult::NO_MATCH}, + + // Federated PSL matches do not apply to Google on HTTP or HTTPS. + {"federation://accounts.google.com/facebook.com", "https://facebook.com", + autofill::PasswordForm::SCHEME_HTML, "https://maps.google.com/", + "https://maps.google.com", MatchResult::NO_MATCH}, + + {"federation://accounts.google.com/facebook.com", "https://facebook.com", + autofill::PasswordForm::SCHEME_HTML, "http://maps.google.com/", + "http://maps.google.com", MatchResult::NO_MATCH}, + + // TLD Mismatch. + {"federation://sub.example.com/google.com", "https://google.com", + autofill::PasswordForm::SCHEME_HTML, "https://sub.example.org/", + "https://sub.example.org", MatchResult::NO_MATCH}, + }; + + for (const TestData& data : cases) { + autofill::PasswordForm form; + form.signon_realm = data.form_signon_realm; + form.federation_origin = url::Origin(GURL(data.form_federation_origin)); + PasswordStore::FormDigest digest( + data.digest_scheme, data.digest_signon_realm, GURL(data.digest_origin)); + + EXPECT_EQ(data.match_result, GetMatchResult(form, digest)) + << "signon_realm = " << data.form_signon_realm + << ", federation_origin = " << data.form_federation_origin + << ", digest = " << digest; + } +} + TEST(PSLMatchingUtilsTest, IsPublicSuffixDomainMatch) { struct TestPair { const char* url1; @@ -86,6 +234,43 @@ } } +TEST(PSLMatchingUtilsTest, IsFederatedPSLMatch) { + struct TestPair { + const char* signon_realm; + const char* origin; + bool should_match; + }; + + const TestPair pairs[] = { + {"https://facebook.com", "https://facebook.com", false}, + {"", "", false}, + {"", "https://facebook.com/", false}, + {"https://facebook.com/", "", false}, + + {"federation://example.com/google.com", "https://example.com/", true}, + {"federation://example.com/google.com", "http://example.com/", false}, + {"federation://example.com/google.com", "example.com", false}, + {"federation://example.com/", "http://example.com/", false}, + {"federation://example.com/google.com", "example", false}, + + {"federation://sub.example.com/google.com", "https://sub.example.com/", + true}, + {"federation://sub1.example.com/google.com", "https://sub2.example.com/", + true}, + {"federation://example.com/google.com", "https://sub.example.com/", true}, + {"federation://example.com/google.com", "http://sub.example.com/", false}, + {"federation://sub.example.com/", "https://sub.example.com/", false}, + }; + + for (const TestPair& pair : pairs) { + std::string signon_realm = pair.signon_realm; + GURL origin(pair.origin); + EXPECT_EQ(pair.should_match, IsFederatedPSLMatch(signon_realm, origin)) + << "signon_realm = " << pair.signon_realm + << ", origin = " << pair.origin; + } +} + } // namespace } // namespace password_manager
diff --git a/content/browser/bluetooth/bluetooth_metrics.cc b/content/browser/bluetooth/bluetooth_metrics.cc index d33f130..84421ee 100644 --- a/content/browser/bluetooth/bluetooth_metrics.cc +++ b/content/browser/bluetooth/bluetooth_metrics.cc
@@ -281,7 +281,7 @@ RecordStartNotificationsOutcome(outcome); return; case UMAGATTOperation::DESCRIPTOR_READ: - // TODO(667319) Add reporting to descriptors + RecordDescriptorReadValueOutcome(outcome); return; case UMAGATTOperation::COUNT: NOTREACHED(); @@ -313,7 +313,6 @@ // Characteristic.readValue -// static void RecordCharacteristicReadValueOutcome(UMAGATTOperationOutcome outcome) { UMA_HISTOGRAM_ENUMERATION("Bluetooth.Web.Characteristic.ReadValue.Outcome", static_cast<int>(outcome), @@ -356,6 +355,19 @@ rssi); } +// Descriptor.readValue + +void RecordDescriptorReadValueOutcome(UMAGATTOperationOutcome outcome) { + UMA_HISTOGRAM_ENUMERATION("Bluetooth.Web.Descriptor.ReadValue.Outcome", + static_cast<int>(outcome), + static_cast<int>(UMAGATTOperationOutcome::COUNT)); +} + +void RecordDescriptorReadValueOutcome(CacheQueryOutcome outcome) { + RecordDescriptorReadValueOutcome( + TranslateCacheQueryOutcomeToGATTOperationOutcome(outcome)); +} + void RecordRSSISignalStrengthLevel(UMARSSISignalStrengthLevel level) { UMA_HISTOGRAM_ENUMERATION( "Bluetooth.Web.RequestDevice.RSSISignalStrengthLevel",
diff --git a/content/browser/bluetooth/bluetooth_metrics.h b/content/browser/bluetooth/bluetooth_metrics.h index 37b54af..65ad7b4f 100644 --- a/content/browser/bluetooth/bluetooth_metrics.h +++ b/content/browser/bluetooth/bluetooth_metrics.h
@@ -35,6 +35,7 @@ REMOTE_GATT_SERVER_DISCONNECT = 8, SERVICE_GET_CHARACTERISTICS = 9, GET_PRIMARY_SERVICES = 10, + DESCRIPTOR_READ_VALUE = 11, // NOTE: Add new actions immediately above this line. Make sure to update // the enum list in tools/metrics/histograms/histograms.xml accordingly. COUNT @@ -278,6 +279,16 @@ // called if QueryCacheForCharacteristic fails. void RecordStartNotificationsOutcome(CacheQueryOutcome outcome); +// Descriptor.readValue() Metrics +// There should be a call to this function for every call to +// Send(BluetoothMsg_ReadDescriptorValueSuccess) and +// Send(BluetoothMsg_ReadDescriptorValueError). +void RecordDescriptorReadValueOutcome(UMAGATTOperationOutcome error); + +// Records the outcome of a cache query for readValue. Should only be called if +// QueryCacheForDescriptor fails. +void RecordDescriptorReadValueOutcome(CacheQueryOutcome outcome); + enum class UMARSSISignalStrengthLevel { LESS_THAN_OR_EQUAL_TO_MIN_RSSI, LEVEL_0,
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc index 15238442..5adce0e 100644 --- a/content/browser/bluetooth/web_bluetooth_service_impl.cc +++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -744,6 +744,8 @@ const std::string& descriptor_instance_id, const RemoteDescriptorReadValueCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + RecordWebBluetoothFunctionCall( + UMAWebBluetoothFunction::DESCRIPTOR_READ_VALUE); const CacheQueryResult query_result = QueryCacheForDescriptor(descriptor_instance_id); @@ -753,12 +755,14 @@ } if (query_result.outcome != CacheQueryOutcome::SUCCESS) { + RecordDescriptorReadValueOutcome(query_result.outcome); callback.Run(query_result.GetWebResult(), base::nullopt /* value */); return; } if (BluetoothBlocklist::Get().IsExcludedFromReads( query_result.descriptor->GetUUID())) { + RecordDescriptorReadValueOutcome(UMAGATTOperationOutcome::BLOCKLISTED); callback.Run(blink::mojom::WebBluetoothResult::BLOCKLISTED_READ, base::nullopt /* value */); return; @@ -976,6 +980,7 @@ const RemoteDescriptorReadValueCallback& callback, const std::vector<uint8_t>& value) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + RecordDescriptorReadValueOutcome(UMAGATTOperationOutcome::SUCCESS); callback.Run(blink::mojom::WebBluetoothResult::SUCCESS, value); } @@ -983,7 +988,6 @@ const RemoteDescriptorReadValueCallback& callback, device::BluetoothRemoteGattService::GattErrorCode error_code) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - // TODO(667319) We are reporting failures to UMA but not reporting successes. callback.Run(TranslateGATTErrorAndRecord(error_code, UMAGATTOperation::DESCRIPTOR_READ), base::nullopt /* value */);
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index 6eb0ca7..e12d29d5 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc
@@ -1534,8 +1534,8 @@ { TRACE_EVENT0("startup", "BrowserMainLoop::BrowserThreadsStarted:InitSpeechRecognition"); - speech_recognition_manager_.reset(new SpeechRecognitionManagerImpl( - audio_manager_.get(), media_stream_manager_.get())); + speech_recognition_manager_.reset( + new SpeechRecognitionManagerImpl(media_stream_manager_.get())); } {
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index b57a31f..4a3b2c7ce 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2806,6 +2806,11 @@ return; } + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableRendererPriorityManagement)) { + return; + } + // We background a process as soon as it hosts no active audio streams and no // visible widgets -- the callers must call this function whenever we // transition in/out of those states.
diff --git a/content/browser/speech/speech_recognition_manager_impl.cc b/content/browser/speech/speech_recognition_manager_impl.cc index a20356b7..c9e470c2 100644 --- a/content/browser/speech/speech_recognition_manager_impl.cc +++ b/content/browser/speech/speech_recognition_manager_impl.cc
@@ -26,7 +26,6 @@ #include "content/public/common/speech_recognition_error.h" #include "content/public/common/speech_recognition_result.h" #include "media/audio/audio_device_description.h" -#include "media/audio/audio_manager.h" #include "url/gurl.h" #include "url/origin.h" @@ -44,11 +43,6 @@ SpeechRecognitionManagerImpl* g_speech_recognition_manager_impl; -void ShowAudioInputSettingsOnFileThread(media::AudioManager* audio_manager) { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - audio_manager->ShowAudioInputSettings(); -} - } // namespace SpeechRecognitionManager* SpeechRecognitionManager::GetInstance() { @@ -67,15 +61,14 @@ } SpeechRecognitionManagerImpl::SpeechRecognitionManagerImpl( - media::AudioManager* audio_manager, - MediaStreamManager* media_stream_manager) - : audio_manager_(audio_manager), - media_stream_manager_(media_stream_manager), + MediaStreamManager* media_stream_manager) + : media_stream_manager_(media_stream_manager), primary_session_id_(kSessionIDInvalid), last_session_id_(kSessionIDInvalid), is_dispatching_event_(false), - delegate_(GetContentClient()->browser()-> - CreateSpeechRecognitionManagerDelegate()), + delegate_(GetContentClient() + ->browser() + ->CreateSpeechRecognitionManagerDelegate()), weak_factory_(this) { DCHECK(!g_speech_recognition_manager_impl); g_speech_recognition_manager_impl = this; @@ -649,18 +642,6 @@ return GetSession(session_id)->config; } -bool SpeechRecognitionManagerImpl::HasAudioInputDevices() { - return audio_manager_->HasAudioInputDevices(); -} - -void SpeechRecognitionManagerImpl::ShowAudioInputSettings() { - // Since AudioManager::ShowAudioInputSettings can potentially launch external - // processes, do that in the FILE thread to not block the calling threads. - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - base::Bind(&ShowAudioInputSettingsOnFileThread, - audio_manager_)); -} - SpeechRecognitionManagerImpl::Session::Session() : id(kSessionIDInvalid), abort_requested(false),
diff --git a/content/browser/speech/speech_recognition_manager_impl.h b/content/browser/speech/speech_recognition_manager_impl.h index 4afc19d..9469016af0b 100644 --- a/content/browser/speech/speech_recognition_manager_impl.h +++ b/content/browser/speech/speech_recognition_manager_impl.h
@@ -19,10 +19,6 @@ #include "content/public/browser/speech_recognition_session_context.h" #include "content/public/common/speech_recognition_error.h" -namespace media { -class AudioManager; -} - namespace content { class BrowserMainLoop; class MediaStreamManager; @@ -72,8 +68,6 @@ int GetSession(int render_process_id, int render_view_id, int request_id) const override; - bool HasAudioInputDevices() override; - void ShowAudioInputSettings() override; // SpeechRecognitionEventListener methods. void OnRecognitionStart(int session_id) override; @@ -98,8 +92,7 @@ friend class BrowserMainLoop; // Needed for dtor. friend std::default_delete<SpeechRecognitionManagerImpl>; - SpeechRecognitionManagerImpl(media::AudioManager* audio_manager, - MediaStreamManager* media_stream_manager); + SpeechRecognitionManagerImpl(MediaStreamManager* media_stream_manager); ~SpeechRecognitionManagerImpl() override; private: @@ -173,7 +166,6 @@ SpeechRecognitionEventListener* GetDelegateListener() const; int GetNextSessionID(); - media::AudioManager* audio_manager_; MediaStreamManager* media_stream_manager_; typedef std::map<int, Session*> SessionsTable; SessionsTable sessions_;
diff --git a/content/public/browser/speech_recognition_manager.h b/content/public/browser/speech_recognition_manager.h index abe1c3a..4e6e9b7 100644 --- a/content/public/browser/speech_recognition_manager.h +++ b/content/public/browser/speech_recognition_manager.h
@@ -75,13 +75,6 @@ int render_view_id, int request_id) const = 0; - // Returns true if the OS reports existence of audio recording devices. - virtual bool HasAudioInputDevices() = 0; - - // Invokes the platform provided microphone settings UI in a non-blocking way, - // via the BrowserThread::FILE thread. - virtual void ShowAudioInputSettings() = 0; - protected: virtual ~SpeechRecognitionManager() {}
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index 1bb64460..69e3577 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc
@@ -270,6 +270,10 @@ // Prevent renderer process backgrounding when set. const char kDisableRendererBackgrounding[] = "disable-renderer-backgrounding"; +// No not manage renderer process priority at all when set. +const char kDisableRendererPriorityManagement[] = + "disable-renderer-priority-management"; + // Whether the resize lock is disabled. Default is false. This is generally only // useful for tests that want to force disabling. const char kDisableResizeLock[] = "disable-resize-lock";
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index e5b8d33..e2d66533 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h
@@ -88,6 +88,7 @@ extern const char kDisableRemoteFonts[]; CONTENT_EXPORT extern const char kDisableRemotePlaybackAPI[]; extern const char kDisableRendererAccessibility[]; +CONTENT_EXPORT extern const char kDisableRendererPriorityManagement[]; CONTENT_EXPORT extern const char kDisableRendererBackgrounding[]; CONTENT_EXPORT extern const char kDisableResizeLock[]; CONTENT_EXPORT extern const char kDisableSeccompFilterSandbox[];
diff --git a/content/public/test/fake_speech_recognition_manager.cc b/content/public/test/fake_speech_recognition_manager.cc index e5e898a..5feb66f 100644 --- a/content/public/test/fake_speech_recognition_manager.cc +++ b/content/public/test/fake_speech_recognition_manager.cc
@@ -126,8 +126,6 @@ DCHECK(delegate_); // We only expect this to be called via |delegate_|. } -bool FakeSpeechRecognitionManager::HasAudioInputDevices() { return true; } - int FakeSpeechRecognitionManager::GetSession(int render_process_id, int render_view_id, int request_id) const {
diff --git a/content/public/test/fake_speech_recognition_manager.h b/content/public/test/fake_speech_recognition_manager.h index b0f3fc4..98a34b8 100644 --- a/content/public/test/fake_speech_recognition_manager.h +++ b/content/public/test/fake_speech_recognition_manager.h
@@ -54,8 +54,6 @@ void AbortAllSessionsForRenderProcess(int render_process_id) override; void AbortAllSessionsForRenderView(int render_process_id, int render_view_id) override; - bool HasAudioInputDevices() override; - void ShowAudioInputSettings() override {} int GetSession(int render_process_id, int render_view_id, int request_id) const override;
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h index ca3aa300..01aed06 100644 --- a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h +++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h
@@ -123,9 +123,9 @@ // WriteRemoteCharacteristic request callbacks. std::pair<base::Closure, ErrorCallback> write_characteristic_value_callbacks_; // Stores SubscribeToNotifications request callbacks. - typedef std::pair<base::Closure, ErrorCallback> PendingNotifyCallback; - // Stores SubscribeToNotifications request callback. - PendingNotifyCallback subscribe_to_notification_callback_; + typedef std::pair<base::Closure, ErrorCallback> PendingNotifyCallbacks; + // Stores SubscribeToNotifications request callbacks. + PendingNotifyCallbacks subscribe_to_notification_callbacks_; // Map of descriptors, keyed by descriptor identifier. std::unordered_map<std::string, std::unique_ptr<BluetoothRemoteGattDescriptorMac>>
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm index 67c4c46a..92d7847c 100644 --- a/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm +++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm
@@ -210,9 +210,9 @@ BluetoothRemoteGattDescriptor* ccc_descriptor, const base::Closure& callback, const ErrorCallback& error_callback) { - DCHECK(subscribe_to_notification_callback_.first.is_null()); - DCHECK(subscribe_to_notification_callback_.second.is_null()); - subscribe_to_notification_callback_ = + DCHECK(subscribe_to_notification_callbacks_.first.is_null()); + DCHECK(subscribe_to_notification_callbacks_.second.is_null()); + subscribe_to_notification_callbacks_ = std::make_pair(callback, error_callback); [GetCBPeripheral() setNotifyValue:YES forCharacteristic:cb_characteristic_.get()]; @@ -294,8 +294,8 @@ void BluetoothRemoteGattCharacteristicMac::DidUpdateNotificationState( NSError* error) { - PendingNotifyCallback reentrant_safe_callbacks; - reentrant_safe_callbacks.swap(subscribe_to_notification_callback_); + PendingNotifyCallbacks reentrant_safe_callbacks; + reentrant_safe_callbacks.swap(subscribe_to_notification_callbacks_); if (error) { VLOG(1) << "Bluetooth error while modifying notification state for " "characteristic, domain: "
diff --git a/ios/chrome/browser/native_app_launcher/BUILD.gn b/ios/chrome/browser/native_app_launcher/BUILD.gn index f85a041b..687ac9db 100644 --- a/ios/chrome/browser/native_app_launcher/BUILD.gn +++ b/ios/chrome/browser/native_app_launcher/BUILD.gn
@@ -101,6 +101,7 @@ "//net:test_support", "//testing/gmock", "//testing/gtest", + "//ui/base", "//url", ] }
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index b627fb9c..f6c062b5 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1593,6 +1593,10 @@ crbug.com/240576 external/wpt/fullscreen/api/element-ready-check-containing-iframe-manual.html [ Timeout Failure Pass ] +# TODO(foolip): Make the timing of fullscreen events reliable. +crbug.com/402376 external/wpt/fullscreen/api/document-exit-fullscreen-timing-manual.html [ Failure Pass ] +crbug.com/402376 external/wpt/fullscreen/api/element-request-fullscreen-timing-manual.html [ Failure Pass ] + crbug.com/567230 [ Debug ] virtual/threaded/animations/restart-not-visible.html [ Timeout ] crbug.com/567419 inspector/elements/styles-2/metrics-box-sizing.html [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/document-exit-fullscreen-twice-manual-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/document-exit-fullscreen-twice-manual-expected.txt new file mode 100644 index 0000000..2124e08 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/document-exit-fullscreen-twice-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Document#exitFullscreen() called twice assert_equals: fullscreenElement after first exitFullscreen() expected Element node <div id="log"></div> but got null +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/document-fullscreen-element-manual-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/document-fullscreen-element-manual-expected.txt new file mode 100644 index 0000000..bb1b685 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/document-fullscreen-element-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Document#fullscreenElement assert_equals: fullscreenElement after requestFullscreen() expected null but got Element node <div id="log"></div> +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-and-move-manual-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-and-move-manual-expected.txt new file mode 100644 index 0000000..ddb6e01 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-and-move-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Element#requestFullscreen() followed by moving the element within the document assert_equals: expected Element node <div id="target"></div> but got null +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-and-move-to-iframe-manual-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-and-move-to-iframe-manual-expected.txt new file mode 100644 index 0000000..bb136d07 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-and-move-to-iframe-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Element#requestFullscreen() followed by moving the element into an iframe assert_unreached: fullscreenchange event Reached unreachable code +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-and-remove-iframe-manual-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-and-remove-iframe-manual-expected.txt new file mode 100644 index 0000000..a522306c --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-and-remove-iframe-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Element#requestFullscreen() in iframe followed by removing the iframe assert_unreached: fullscreenchange event Reached unreachable code +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-and-remove-manual-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-and-remove-manual-expected.txt new file mode 100644 index 0000000..19293294 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-and-remove-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Element#requestFullscreen() followed by removing the element assert_unreached: fullscreenchange event Reached unreachable code +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-twice-manual-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-twice-manual-expected.txt new file mode 100644 index 0000000..a6b832f --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/fullscreen/api/element-request-fullscreen-twice-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Element#requestFullscreen() twice assert_equals: fullscreenElement after first requestFullscreen() expected null but got Element node <div id="log"></div> +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-auto-repeat-huge-grid.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-auto-repeat-huge-grid.html new file mode 100644 index 0000000..6aa75a5 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-auto-repeat-huge-grid.html
@@ -0,0 +1,149 @@ +<!DOCTYPE html> +<title>Test for auto-fit and auto-fill with huge grids (lots of tracks)</title> +<link href="resources/grid.css" rel="stylesheet"> +<link href="../css-intrinsic-dimensions/resources/width-keyword-classes.css" rel="stylesheet"> +<link href="../css-intrinsic-dimensions/resources/height-keyword-classes.css" rel="stylesheet"> + +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="resources/grid-definitions-parsing-utils.js"></script> +<style> +.wideGrid { width: 1000000000px; } +.tallGrid { height: 1000000000px; } + +.minSizeWideGrid { min-width: 1000000000px; } +.minSizeTallGrid { min-height: 1000000000px; } + +.lotsOfFixedRepeatWithAutoFitCols { grid-template-columns: repeat(auto-fit, 10px 2px 8px) repeat(992, 1px); } +.lotsOfFixedRepeatWithAutoFillCols { grid-template-columns: repeat(auto-fill, 10px 2px 8px 7px 20px) repeat(995, 1px); } + +.lotsOfAutoRepeatWithAutoFitCols { grid-template-columns: repeat(auto-fit, 10px 2px 8px) repeat(10, 1px); } +.lotsOfAutoRepeatWithAutoFillCols { grid-template-columns: repeat(auto-fill, 10px 2px 8px 7px 20px) repeat(10, 1px); } + +.lotsOfFixedRepeatWithAutoFitRows { grid-template-rows: repeat(auto-fit, 10px 2px 8px) repeat(992, 1px); } +.lotsOfFixedRepeatWithAutoFillRows { grid-template-rows: repeat(auto-fill, 10px 2px 8px 7px 20px) repeat(995, 1px); } + +.lotsOfAutoRepeatWithAutoFitRows { grid-template-rows: repeat(auto-fit, 10px 2px 8px) repeat(10, 1px); } +.lotsOfAutoRepeatWithAutoFillRows { grid-template-rows: repeat(auto-fill, 10px 2px 8px 7px 20px) repeat(10, 1px); } +</style> + +<div id="wideAutoFillGrid" class="grid wideGrid lotsOfFixedRepeatWithAutoFillCols"></div> +<div id="wideAutoFitGrid" class="grid wideGrid lotsOfFixedRepeatWithAutoFitCols"> + <div>Item1</div> + <div>Item2</div> + <div>Item3</div> +</div> + +<div id="tallAutoFillGrid" class="grid tallGrid lotsOfFixedRepeatWithAutoFillRows"></div> +<div id="tallAutoFitGrid" class="grid tallGrid lotsOfFixedRepeatWithAutoFitRows"> + <div>Item1</div> + <div>Item2</div> + <div>Item3</div> +</div> + +<div id="wideAutoFillGridFewRepetitions" class="grid wideGrid lotsOfAutoRepeatWithAutoFillCols"></div> +<div id="wideAutoFitGridFewRepetitions" class="grid wideGrid lotsOfAutoRepeatWithAutoFitCols"> + <div>Item1</div> + <div>Item2</div> + <div>Item3</div> +</div> + +<div id="tallAutoFillGridFewRepetitions" class="grid tallGrid lotsOfAutoRepeatWithAutoFillRows"></div> +<div id="tallAutoFitGridFewRepetitions" class="grid tallGrid lotsOfAutoRepeatWithAutoFitRows"> + <div>Item1</div> + <div>Item2</div> + <div>Item3</div> +</div> + +<div id="wideAutoFillGridFewRepetitionsMinSize" class="grid lotsOfAutoRepeatWithAutoFillCols minSizeWideGrid min-content"></div> +<div id="wideAutoFitGridFewRepetitionsMinSize" class="grid lotsOfAutoRepeatWithAutoFitCols minSizeWideGrid min-content"> + <div>Item1</div> + <div>Item2</div> + <div>Item3</div> +</div> + +<div id="tallAutoFillGridFewRepetitionsMinSize" class="grid lotsOfAutoRepeatWithAutoFillRows minSizeTallGrid min-content"></div> +<div id="tallAutoFitGridFewRepetitionsMinSize" class="grid lotsOfAutoRepeatWithAutoFitRows minSizeTallGrid min-content"> + <div>Item1</div> + <div>Item2</div> + <div>Item3</div> +</div> + +<script> +function testElement(element, property, length) { + var tracks = getComputedStyle(document.getElementById(element), '').getPropertyValue(property).split(' '); + assert_equals(tracks.length, length); + return tracks; +} + +test(function() { + testElement("wideAutoFillGrid", "grid-template-columns", 1000); + testElement("wideAutoFitGrid", "grid-template-columns", 998); +}, "Test that we don't get more than kGridMaxTracks repetitions even on very wide grids."); + +test(function() { + testElement("tallAutoFillGrid", "grid-template-rows", 1000); + testElement("tallAutoFitGrid", "grid-template-rows", 998); +}, "Test that we don't get more than kGridMaxTracks repetitions even on very tall grids."); + +test(function() { + var wideAutoFillGrid = document.getElementById("wideAutoFillGridFewRepetitions"); + var wideAutoFitGrid = document.getElementById("wideAutoFitGridFewRepetitions"); + + wideAutoFillGrid.style.gridGap = "100px"; + wideAutoFitGrid.style.gridGap = "100px"; + + testElement("wideAutoFillGridFewRepetitions", "grid-template-columns", 1000); + testElement("wideAutoFitGridFewRepetitions", "grid-template-columns", 1000); + + wideAutoFillGrid.style.gridGap = "1000000px"; + wideAutoFitGrid.style.gridGap = "1000000px"; + + testElement("wideAutoFillGridFewRepetitions", "grid-template-columns", 130); + testElement("wideAutoFitGridFewRepetitions", "grid-template-columns", 82); + + wideAutoFillGrid.style.gridGap = "0px"; + wideAutoFitGrid.style.gridGap = "0px"; +}, "Test that we don't get more than kGridMaxTracks repetitions even on very wide grids with gaps."); + +test(function() { + var tallAutoFillGrid = document.getElementById("tallAutoFillGridFewRepetitions"); + var tallAutoFitGrid = document.getElementById("tallAutoFitGridFewRepetitions"); + + tallAutoFillGrid.style.gridGap = "100px"; + tallAutoFitGrid.style.gridGap = "100px"; + + testElement("tallAutoFillGridFewRepetitions", "grid-template-rows", 1000); + testElement("tallAutoFitGridFewRepetitions", "grid-template-rows", 1000); + + tallAutoFillGrid.style.gridGap = "1000000px"; + tallAutoFitGrid.style.gridGap = "1000000px"; + + testElement("tallAutoFillGridFewRepetitions", "grid-template-rows", 130); + testElement("tallAutoFitGridFewRepetitions", "grid-template-rows", 82); + + tallAutoFillGrid.style.gridGap = "0px"; + tallAutoFitGrid.style.gridGap = "0px"; +}, "Test that we don't get more than kGridMaxTracks repetitions even on very tall grids with gaps."); + + test(function() { + var autoFillCols = testElement("wideAutoFillGridFewRepetitionsMinSize", "grid-template-columns", 1000); + var autoFitCols = testElement("wideAutoFitGridFewRepetitionsMinSize", "grid-template-columns", 1000); + + /* Check that clamping auto repetitions does not reduce the amount of the other tracks. */ + assert_equals(autoFillCols[1000 - 10 - 1], "20px"); + assert_equals(autoFillCols[1000 - 10], "1px"); + assert_equals(autoFitCols[1000 - 10 - 1], "0px"); + assert_equals(autoFitCols[1000 - 10], "1px"); + + var autoFillRows = testElement("tallAutoFillGridFewRepetitionsMinSize", "grid-template-rows", 1000); + var autoFitRows = testElement("tallAutoFitGridFewRepetitionsMinSize", "grid-template-rows", 1000); + + /* Check that clamping auto repetitions does not reduce the amount of the other tracks. */ + assert_equals(autoFillRows[1000 - 10 - 1], "20px"); + assert_equals(autoFillRows[1000 - 10], "1px"); + assert_equals(autoFitRows[1000 - 10 - 1], "0px"); + assert_equals(autoFitRows[1000 - 10], "1px"); + }, "Test that we don't get more than kGridMaxTracks repetitions even on very wide grids with gaps and min-width."); + +</script>
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/document-exit-fullscreen-twice.html b/third_party/WebKit/LayoutTests/fullscreen/api/document-exit-fullscreen-twice.html new file mode 100644 index 0000000..fbf429eb --- /dev/null +++ b/third_party/WebKit/LayoutTests/fullscreen/api/document-exit-fullscreen-twice.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<title>Document#exitFullscreen() called twice</title> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="../trusted-click.js"></script> +<div id="log"></div> +<script> +// Adapted from https://github.com/w3c/web-platform-tests/pull/4250 +// TODO(foolip): Remove this test when the above is imported and passing. +async_test(t => { + const div = document.querySelector("div"); + + document.onfullscreenchange = t.step_func(() => { + // We are now in fullscreen. + assert_equals(document.fullscreenElement, div); + + document.onfullscreenchange = t.step_func(() => { + assert_equals(document.fullscreenElement, null); + // Done, but ensure that there's only one fullscreenchange event. + document.onfullscreenchange = t.unreached_func("second fullscreenchange event"); + setTimeout(t.step_func_done(), 0); + }); + + // Exit fullscreen twice. + document.exitFullscreen(); + assert_equals(document.fullscreenElement, null, "fullscreenElement after first exitFullscreen()"); + document.exitFullscreen(); + assert_equals(document.fullscreenElement, null, "fullscreenElement after second exitFullscreen()"); + }); + document.onfullscreenerror = t.unreached_func("fullscreenerror event"); + + trusted_request(div); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/document-exit-fullscreen-vs-request.html b/third_party/WebKit/LayoutTests/fullscreen/api/document-exit-fullscreen-vs-request.html index 2c402e5..54f2afc 100644 --- a/third_party/WebKit/LayoutTests/fullscreen/api/document-exit-fullscreen-vs-request.html +++ b/third_party/WebKit/LayoutTests/fullscreen/api/document-exit-fullscreen-vs-request.html
@@ -4,14 +4,12 @@ <script src="../../resources/testharnessreport.js"></script> <script src="../trusted-click.js"></script> <div id="log"></div> -<div id="parent"><div><div></div></div></div> +<div id="parent"><div></div></div> <script> // Adapted from https://github.com/w3c/web-platform-tests/pull/4250 // TODO(foolip): Remove this test when the above is imported and passing. async_test(t => { const parent = document.getElementById("parent"); - const child = parent.firstChild; - const grandChild = child.firstChild; document.onfullscreenchange = t.step_func(() => { // We are now in fullscreen, so exiting requires a resize but requesting @@ -22,26 +20,23 @@ // Request fullscreen on another element, to avoid any synchronous // short-circuiting on document.fullscreenElement.requestFullscreen(), // which used to be in the spec. Also request both before and after the - // exit. Both requests synchronously enqueue animation frame tasks. They - // may run after exiting, but still before the animation frame task for - // the exit, and so both will succeed, and there will be 3 - // fullscreenchange events, but not matching the order of the calls. + // exit. Both requests should be silently ignored due to the exit. - let i = 0; - const expected = [child, grandChild, null]; - document.onfullscreenchange = t.step_func(() => { - assert_equals(document.fullscreenElement, expected[i], "fullscreenElement when i=" + i); - i++; - if (i == 3) + let fullscreenchanges = 0; + document.onfullscreenchange = t.step_func((event) => { + assert_equals(document.fullscreenElement, child); + fullscreenchanges++; + if (fullscreenchanges == 3) t.done(); }); + const child = parent.firstChild; child.requestFullscreen(); - assert_equals(document.fullscreenElement, parent, "fullscreenElement after first requestFullscreen()"); + assert_equals(document.fullscreenElement, child, "fullscreenElement after first requestFullscreen()"); document.exitFullscreen(); assert_equals(document.fullscreenElement, parent, "fullscreenElement after exitFullscreen()"); - grandChild.requestFullscreen(); - assert_equals(document.fullscreenElement, parent, "fullscreenElement after second requestFullscreen()"); + child.requestFullscreen(); + assert_equals(document.fullscreenElement, child, "fullscreenElement after second requestFullscreen()"); }), parent); }); document.onfullscreenerror = t.unreached_func("fullscreenerror event");
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/document-fullscreen-element.html b/third_party/WebKit/LayoutTests/fullscreen/api/document-fullscreen-element.html new file mode 100644 index 0000000..f339c0b --- /dev/null +++ b/third_party/WebKit/LayoutTests/fullscreen/api/document-fullscreen-element.html
@@ -0,0 +1,36 @@ +<!DOCTYPE html> +<title>Document.fullscreenElement</title> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="../trusted-click.js"></script> +<div id="log"></div> +<script> +async_test(function(t) +{ + var div = document.querySelector("div"); + + document.onfullscreenchange = t.step_func(function() + { + assert_equals(document.fullscreenElement, div, "fullscreenElement before exitFullscreen()"); + document.exitFullscreen(); + // TODO(foolip): fullscreenElement should still be div. + // https://crbug.com/402421 + assert_equals(document.fullscreenElement, null, "fullscreenElement after exitFullscreen()"); + + document.onfullscreenchange = t.step_func(function() + { + assert_equals(document.fullscreenElement, null, "fullscreenElement after exiting fullscreen"); + t.done(); + }); + }); + + trusted_click(t.step_func(function() + { + assert_equals(document.fullscreenElement, null, "fullscreenElement before requestFullscreen()"); + div.requestFullscreen(); + // TODO(foolip): fullscreenElement should still be null. + // https://crbug.com/402421 + assert_equals(document.fullscreenElement, div, "fullscreenElement after requestFullscreen()"); + }), document.body); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-move-to-iframe-prefixed.html b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-move-to-iframe-prefixed.html deleted file mode 100644 index ef873a6..0000000 --- a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-move-to-iframe-prefixed.html +++ /dev/null
@@ -1,28 +0,0 @@ -<!DOCTYPE html> -<title>Element#webkitRequestFullscreen() followed by moving the element into an iframe</title> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../trusted-click.js"></script> -<div id="log"></div> -<div id="target"></div> -<iframe allowfullscreen></iframe> -<script> -async_test(t => { - const target = document.getElementById("target"); - const iframeDoc = document.querySelector("iframe").contentDocument; - - iframeDoc.onwebkitfullscreenchange = t.unreached_func("webkitfullscreenchange event in iframe"); - iframeDoc.onwebkitfullscreenerror = t.unreached_func("webkitfullscreenerror event in iframe"); - document.onwebkitfullscreenchange = t.unreached_func("webkitfullscreenchange event"); - document.onwebkitfullscreenerror = t.step_func_done(event => { - assert_equals(event.target, document); - assert_equals(document.webkitFullscreenElement, null); - assert_equals(iframeDoc.webkitFullscreenElement, null); - }); - - trusted_click(t.step_func(() => { - target.webkitRequestFullscreen(); - iframeDoc.body.appendChild(target); - }), document.body); -}); -</script>
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-move-to-iframe.html b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-move-to-iframe.html new file mode 100644 index 0000000..1a8c72f --- /dev/null +++ b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-move-to-iframe.html
@@ -0,0 +1,29 @@ +<!DOCTYPE html> +<title>Element#requestFullscreen() followed by moving the element into an iframe</title> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="../trusted-click.js"></script> +<div id="log"></div> +<div id="target"></div> +<iframe allowfullscreen></iframe> +<script> +// Adapted from https://github.com/w3c/web-platform-tests/pull/4250 +// TODO(foolip): Remove this test when the above is imported and passing. +async_test(t => { + const target = document.getElementById("target"); + const iframeDoc = document.querySelector("iframe").contentDocument; + + iframeDoc.onfullscreenchange = t.unreached_func("fullscreenchange event in iframe"); + iframeDoc.onfullscreenerror = t.unreached_func("fullscreenerror event in iframe"); + document.onfullscreenchange = t.step_func_done(() => { + assert_equals(document.fullscreenElement, null); + assert_equals(iframeDoc.fullscreenElement, null); + }); + document.onfullscreenerror = t.unreached_func("fullscreenerror event"); + + trusted_click(t.step_func(() => { + target.requestFullscreen(); + iframeDoc.body.appendChild(target); + }), document.body); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-move.html b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-move.html new file mode 100644 index 0000000..5189566 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-move.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<title>Element#requestFullscreen() followed by moving the element within the document</title> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="../trusted-click.js"></script> +<div id="log"></div> +<div id="target"></div> +<div id="moveto"></div> +<script> +// Adapted from https://github.com/w3c/web-platform-tests/pull/4250 +// TODO(foolip): Remove this test when the above is imported and passing. +async_test(t => { + const target = document.getElementById("target"); + const moveTo = document.getElementById("moveto"); + + document.onfullscreenchange = t.step_func_done(() => { + assert_equals(document.fullscreenElement, null); + assert_equals(target.parentNode, moveTo); + }); + document.onfullscreenerror = t.unreached_func("fullscreenchange event"); + + trusted_click(t.step_func(() => { + target.requestFullscreen(); + moveTo.appendChild(target); + }), document.body); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-remove-iframe.html b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-remove-iframe.html new file mode 100644 index 0000000..3edb7c8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-remove-iframe.html
@@ -0,0 +1,28 @@ +<!DOCTYPE html> +<title>Element#requestFullscreen() in iframe followed by removing the iframe</title> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="../trusted-click.js"></script> +<div id="log"></div> +<iframe allowfullscreen></iframe> +<script> +// Adapted from https://github.com/w3c/web-platform-tests/pull/4250 +// TODO(foolip): Remove this test when the above is imported and passing. +async_test(t => { + const iframe = document.querySelector("iframe"); + const iframeDocument = iframe.contentDocument; + + document.onfullscreenchange = t.step_func_done(() => { + assert_equals(document.fullscreenElement, null); + assert_equals(iframeDocument.fullscreenElement, null); + }); + document.onfullscreenerror = t.unreached_func("fullscreenerror event"); + iframeDocument.onfullscreenchange = t.unreached_func("iframe fullscreenchange event"); + iframeDocument.onfullscreenerror = t.unreached_func("iframe fullscreenerror event"); + + trusted_click(t.step_func(() => { + iframeDocument.body.requestFullscreen(); + iframe.remove(); + }), document.body); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-remove-prefixed.html b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-remove-prefixed.html deleted file mode 100644 index 62a78e6c..0000000 --- a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-remove-prefixed.html +++ /dev/null
@@ -1,23 +0,0 @@ -<!DOCTYPE html> -<title>Element#webkitRequestFullscreen() followed by removing the element</title> -<script src="../../resources/testharness.js"></script> -<script src="../../resources/testharnessreport.js"></script> -<script src="../trusted-click.js"></script> -<div id="log"></div> -<div id="target"></div> -<script> -async_test(t => { - const target = document.getElementById("target"); - - document.onwebkitfullscreenchange = t.unreached_func("webkitfullscreenchange event"); - document.onwebkitfullscreenerror = t.step_func_done(event => { - assert_equals(event.target, document); - assert_equals(document.webkitFullscreenElement, null); - }); - - trusted_click(t.step_func(() => { - target.webkitRequestFullscreen(); - target.remove(); - }), document.body); -}); -</script>
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-remove.html b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-remove.html new file mode 100644 index 0000000..f5bb0e9 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-and-remove.html
@@ -0,0 +1,24 @@ +<!DOCTYPE html> +<title>Element#requestFullscreen() followed by removing the element</title> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="../trusted-click.js"></script> +<div id="log"></div> +<div id="target"></div> +<script> +// Adapted from https://github.com/w3c/web-platform-tests/pull/4250 +// TODO(foolip): Remove this test when the above is imported and passing. +async_test(t => { + const target = document.getElementById("target"); + + document.onfullscreenchange = t.step_func_done(() => { + assert_equals(document.fullscreenElement, null); + }); + document.onfullscreenerror = t.unreached_func("fullscreenchange event"); + + trusted_click(t.step_func(() => { + target.requestFullscreen(); + target.remove(); + }), document.body); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-twice.html b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-twice.html new file mode 100644 index 0000000..80d1761e --- /dev/null +++ b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-twice.html
@@ -0,0 +1,29 @@ +<!DOCTYPE html> +<title>Element#requestFullscreen() twice</title> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script src="../trusted-click.js"></script> +<div id="log"></div> +<script> +// Adapted from https://github.com/w3c/web-platform-tests/pull/4250 +// TODO(foolip): Remove this test when the above is imported and passing. +async_test(t => { + const div = document.querySelector("div"); + + document.onfullscreenchange = t.step_func(() => { + assert_equals(document.fullscreenElement, div); + // Done, but ensure that there's only one fullscreenchange event. + document.onfullscreenchange = t.unreached_func("second fullscreenchange event"); + setTimeout(t.step_func_done(), 0); + }); + document.onfullscreenerror = t.unreached_func("fullscreenerror event"); + + trusted_click(t.step_func(() => { + // Request fullscreen twice. + div.requestFullscreen(); + assert_equals(document.fullscreenElement, div, "fullscreenElement after first requestFullscreen()"); + div.requestFullscreen(); + assert_equals(document.fullscreenElement, div, "fullscreenElement after second requestFullscreen()"); + }), document.body); +}); +</script>
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-two-iframes.html b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-two-iframes.html index 688e669..c9b0ed6a 100644 --- a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-two-iframes.html +++ b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-two-iframes.html
@@ -18,15 +18,15 @@ // Expect first a fullscreenchange event for the second (!) request, then a // fullscreenerror event for the first request. TODO(foolip): Remove the // Fullscreen hierarchy restrictions. https://crbug.com/627792 - a.contentDocument.onfullscreenchange = t.step_func_done(() => { - assert_equals(document.fullscreenElement, a, 'fullscreenElement'); - assert_equals(a.contentDocument.fullscreenElement, a.contentDocument.body, 'fullscreenElement in iframe a'); - b.contentDocument.onfullscreenerror = t.step_func(() => { - assert_equals(b.contentDocument.fullscreenElement, null, 'fullscreenElement in iframe b'); + a.contentDocument.onfullscreenerror = t.step_func(() => { + b.contentDocument.onfullscreenchange = t.step_func_done(() => { + assert_equals(document.fullscreenElement, b, 'fullscreenElement'); + assert_equals(a.contentDocument.fullscreenElement, null, 'fullscreenElement in iframe a'); + assert_equals(b.contentDocument.fullscreenElement, b.contentDocument.body, 'fullscreenElement in iframe b'); }); }); - a.contentDocument.onfullscreenerror = t.unreached_func('fullscreenerror event in iframe a'); - b.contentDocument.onfullscreenchange = t.unreached_func('fullscreenchange event in iframe b'); + a.contentDocument.onfullscreenchange = t.unreached_func('fullscreenchange event in iframe a'); + b.contentDocument.onfullscreenerror = t.unreached_func('fullscreenerror event in iframe b'); trusted_click(t.step_func(() => { b.contentDocument.body.requestFullscreen();
diff --git a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-vs-exit.html b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-vs-exit.html index ad5ed1e..a766a6f 100644 --- a/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-vs-exit.html +++ b/third_party/WebKit/LayoutTests/fullscreen/api/element-request-fullscreen-vs-exit.html
@@ -18,7 +18,7 @@ document.onfullscreenerror = t.unreached_func("fullscreenerror event"); target.requestFullscreen(); - assert_equals(document.fullscreenElement, null, "fullscreenElement after requestFullscreen()"); + assert_equals(document.fullscreenElement, target, "fullscreenElement after requestFullscreen()"); document.exitFullscreen(); assert_equals(document.fullscreenElement, null, "fullscreenElement after exitFullscreen()"); }), document.body);
diff --git a/third_party/WebKit/LayoutTests/fullscreen/enter-exit-full-screen-hover-expected.txt b/third_party/WebKit/LayoutTests/fullscreen/enter-exit-full-screen-hover-expected.txt index 9388a98..93985ea 100644 --- a/third_party/WebKit/LayoutTests/fullscreen/enter-exit-full-screen-hover-expected.txt +++ b/third_party/WebKit/LayoutTests/fullscreen/enter-exit-full-screen-hover-expected.txt
@@ -2,10 +2,8 @@ TEST COMPLETE PASS document.webkitIsFullScreen is true -PASS getHoverActiveState(enterButton) is "hovered" PASS getHoverActiveState(enterButton) is "default" PASS document.webkitIsFullScreen is false -PASS getHoverActiveState(exitButton) is "hovered" PASS getHoverActiveState(exitButton) is "default" Go full screen Exit full screen EVENT(webkitfullscreenchange)
diff --git a/third_party/WebKit/LayoutTests/fullscreen/enter-exit-full-screen-hover.html b/third_party/WebKit/LayoutTests/fullscreen/enter-exit-full-screen-hover.html index 85f4a27..1aa60aab 100644 --- a/third_party/WebKit/LayoutTests/fullscreen/enter-exit-full-screen-hover.html +++ b/third_party/WebKit/LayoutTests/fullscreen/enter-exit-full-screen-hover.html
@@ -23,30 +23,22 @@ var enterButtonCenter = elementCenter(enterButton); waitForEventOnce(document, 'webkitfullscreenchange', function() { - shouldBeTrue("document.webkitIsFullScreen"); - // After entering fullscreen + layout, the button should lose hover. - // TODO(foolip): Synchronize hover state changes with animation frames. - // https://crbug.com/668758 - shouldBeOnlyHovered("getHoverActiveState(enterButton)"); - testRunner.layoutAndPaintAsyncThen(function() { - shouldBeDefault("getHoverActiveState(enterButton)"); + shouldBeTrue("document.webkitIsFullScreen") + // After entering fullscreen, the button should lose hover + shouldBeDefault("getHoverActiveState(enterButton)") - waitForEventOnce(document, 'webkitfullscreenchange', function() { - shouldBeFalse("document.webkitIsFullScreen"); - // After exiting fullscreen + layout, the button should lose hover. - shouldBeOnlyHovered("getHoverActiveState(exitButton)"); - testRunner.layoutAndPaintAsyncThen(function() { - shouldBeDefault("getHoverActiveState(exitButton)"); - endTest(); - }); - }); - - var exitButtonCenter = elementCenter(exitButton); - // Hover on and click the "Exit fullscreen" button - eventSender.mouseMoveTo(exitButtonCenter.x, exitButtonCenter.y); - eventSender.mouseDown(); - eventSender.mouseUp(); + waitForEventOnce(document, 'webkitfullscreenchange', function() { + shouldBeFalse("document.webkitIsFullScreen") + // After leaving fullscreen, the button should lose hover + shouldBeDefault("getHoverActiveState(exitButton)") + endTest(); }); + + var exitButtonCenter = elementCenter(exitButton); + // Hover on and click the "Exit fullscreen" button + eventSender.mouseMoveTo(exitButtonCenter.x, exitButtonCenter.y); + eventSender.mouseDown(); + eventSender.mouseUp(); });
diff --git a/third_party/WebKit/LayoutTests/fullscreen/full-screen-remove-ancestor-during-transition-expected.txt b/third_party/WebKit/LayoutTests/fullscreen/full-screen-remove-ancestor-during-transition-expected.txt new file mode 100644 index 0000000..b5c1b43d --- /dev/null +++ b/third_party/WebKit/LayoutTests/fullscreen/full-screen-remove-ancestor-during-transition-expected.txt
@@ -0,0 +1,5 @@ +PASS successfullyParsed is true + +TEST COMPLETE +PASS document.webkitFullscreenElement is not null +PASS
diff --git a/third_party/WebKit/LayoutTests/fullscreen/full-screen-remove-ancestor-during-transition.html b/third_party/WebKit/LayoutTests/fullscreen/full-screen-remove-ancestor-during-transition.html new file mode 100644 index 0000000..735c5204 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fullscreen/full-screen-remove-ancestor-during-transition.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<script src="../resources/js-test.js"></script> +<script> +if (window.testRunner) { + testRunner.dumpAsText(); + testRunner.waitUntilDone(); +} + +function runWithKeyDown(fn) +{ + document.addEventListener('keydown', function() { fn(); }, false); + if (window.testRunner) { + eventSender.keyDown('a'); + } +} + +function init() { + runWithKeyDown(goFullScreen); +} + +function goFullScreen() { + var iframe = document.getElementById('block1'); + var element = iframe.contentDocument.documentElement; + setTimeout(function () { + iframe.parentNode.removeChild(iframe); + gc(); + setTimeout(function () { + if (window.testRunner) { + testRunner.notifyDone(); + } + }, 0); + }, 0); + element.webkitRequestFullScreen(); + shouldNotBe("document.webkitFullscreenElement", "null"); +} +</script> +<body onload="init()"> + <iframe allowfullscreen src="resources/inner.html" id="block1"></iframe> + PASS +</body>
diff --git a/third_party/WebKit/LayoutTests/fullscreen/full-screen-stacking-context.html b/third_party/WebKit/LayoutTests/fullscreen/full-screen-stacking-context.html index 56bd356..34f58dfe 100644 --- a/third_party/WebKit/LayoutTests/fullscreen/full-screen-stacking-context.html +++ b/third_party/WebKit/LayoutTests/fullscreen/full-screen-stacking-context.html
@@ -5,9 +5,7 @@ var runPixelTests = true; function init() { - waitForEventOnce(document, 'webkitfullscreenchange', function() { - testRunner.layoutAndPaintAsyncThen(endTest); - }); + waitForEventAndEnd(document, 'webkitfullscreenchange'); runWithKeyDown(goFullScreen); }
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/document-exit-fullscreen-twice-manual-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/document-exit-fullscreen-twice-manual-expected.txt new file mode 100644 index 0000000..2124e08 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/document-exit-fullscreen-twice-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Document#exitFullscreen() called twice assert_equals: fullscreenElement after first exitFullscreen() expected Element node <div id="log"></div> but got null +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/document-fullscreen-element-manual-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/document-fullscreen-element-manual-expected.txt new file mode 100644 index 0000000..bb1b685 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/document-fullscreen-element-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Document#fullscreenElement assert_equals: fullscreenElement after requestFullscreen() expected null but got Element node <div id="log"></div> +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-and-move-manual-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-and-move-manual-expected.txt new file mode 100644 index 0000000..ddb6e01 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-and-move-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Element#requestFullscreen() followed by moving the element within the document assert_equals: expected Element node <div id="target"></div> but got null +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-and-move-to-iframe-manual-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-and-move-to-iframe-manual-expected.txt new file mode 100644 index 0000000..bb136d07 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-and-move-to-iframe-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Element#requestFullscreen() followed by moving the element into an iframe assert_unreached: fullscreenchange event Reached unreachable code +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-and-remove-iframe-manual-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-and-remove-iframe-manual-expected.txt new file mode 100644 index 0000000..a522306c --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-and-remove-iframe-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Element#requestFullscreen() in iframe followed by removing the iframe assert_unreached: fullscreenchange event Reached unreachable code +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-and-remove-manual-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-and-remove-manual-expected.txt new file mode 100644 index 0000000..19293294 --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-and-remove-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Element#requestFullscreen() followed by removing the element assert_unreached: fullscreenchange event Reached unreachable code +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-twice-manual-expected.txt b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-twice-manual-expected.txt new file mode 100644 index 0000000..a6b832f --- /dev/null +++ b/third_party/WebKit/LayoutTests/imported/wpt/fullscreen/api/element-request-fullscreen-twice-manual-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Element#requestFullscreen() twice assert_equals: fullscreenElement after first requestFullscreen() expected null but got Element node <div id="log"></div> +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/Source/bindings/core/v8/custom/V8WindowCustom.cpp b/third_party/WebKit/Source/bindings/core/v8/custom/V8WindowCustom.cpp index 5c9bf858..774a91e6f 100644 --- a/third_party/WebKit/Source/bindings/core/v8/custom/V8WindowCustom.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/custom/V8WindowCustom.cpp
@@ -51,6 +51,7 @@ #include "core/frame/ImageBitmap.h" #include "core/frame/LocalDOMWindow.h" #include "core/frame/LocalFrame.h" +#include "core/frame/Location.h" #include "core/frame/Settings.h" #include "core/frame/UseCounter.h" #include "core/frame/csp/ContentSecurityPolicy.h" @@ -65,6 +66,58 @@ namespace blink { +void V8Window::locationAttributeGetterCustom( + const v8::PropertyCallbackInfo<v8::Value>& info) { + v8::Isolate* isolate = info.GetIsolate(); + v8::Local<v8::Object> holder = info.Holder(); + + DOMWindow* window = V8Window::toImpl(holder); + Location* location = window->location(); + DCHECK(location); + + // Keep the wrapper object for the return value alive as long as |this| + // object is alive in order to save creation time of the wrapper object. + if (DOMDataStore::setReturnValue(info.GetReturnValue(), location)) + return; + + v8::Local<v8::Value> wrapper; + + // Note that this check is gated on whether or not |window| is remote, not + // whether or not |window| is cross-origin. If |window| is local, the + // |location| property must always return the same wrapper, even if the + // cross-origin status changes by changing properties like |document.domain|. + if (window->isRemoteDOMWindow()) { + DOMWrapperWorld& world = DOMWrapperWorld::current(isolate); + const auto* wrapperTypeInfo = location->wrapperTypeInfo(); + v8::Local<v8::Object> newWrapper = + wrapperTypeInfo->domTemplate(isolate, world) + ->NewRemoteInstance() + .ToLocalChecked(); + + DCHECK(!DOMDataStore::containsWrapper(location, isolate)); + wrapper = V8DOMWrapper::associateObjectWithWrapper( + isolate, location, wrapperTypeInfo, newWrapper); + } else { + wrapper = ToV8(location, holder, isolate); + } + + // Keep the wrapper object for the return value alive as long as |this| + // object is alive in order to save creation time of the wrapper object. + // + // TODO(dcheng): The hidden reference behavior is broken in many ways. We + // should be caching for all DOM attributes. Even if it's not critical for + // remote Location objects, we should clean this up to improve + // maintainability. In the long-term, this will be superseded by wrapper + // tracing. + const char kKeepAliveKey[] = "KeepAlive#Window#location"; + V8HiddenValue::setHiddenValue( + ScriptState::current(isolate), holder, + v8AtomicString(isolate, StringView(kKeepAliveKey, sizeof kKeepAliveKey)), + wrapper); + + v8SetReturnValue(info, wrapper); +} + void V8Window::eventAttributeGetterCustom( const v8::FunctionCallbackInfo<v8::Value>& info) { LocalDOMWindow* impl = toLocalDOMWindow(V8Window::toImpl(info.Holder()));
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_attributes.py b/third_party/WebKit/Source/bindings/scripts/v8_attributes.py index 7eed0bd..109b010 100644 --- a/third_party/WebKit/Source/bindings/scripts/v8_attributes.py +++ b/third_party/WebKit/Source/bindings/scripts/v8_attributes.py
@@ -198,8 +198,6 @@ # [CrossOrigin] is incompatible with a number of other attributes, so check # for them here. if is_cross_origin: - if context['has_cross_origin_getter'] and context['has_custom_getter']: - raise Exception('[CrossOrigin] and [Custom] are incompatible on the same getter: %s.%s', interface.name, attribute.name) if context['has_cross_origin_setter'] and context['has_custom_setter']: raise Exception('[CrossOrigin] and [Custom] are incompatible on the same setter: %s.%s', interface.name, attribute.name) if context['is_per_world_bindings']:
diff --git a/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl index 31c8bebb..0a768e6 100644 --- a/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl +++ b/third_party/WebKit/Source/bindings/templates/interface_base.cpp.tmpl
@@ -162,7 +162,15 @@ {% for attribute in attributes if attribute.has_cross_origin_getter or attribute.has_cross_origin_setter %} { "{{attribute.name}}", - {%+ if attribute.has_cross_origin_getter %}&{{cpp_class}}V8Internal::{{attribute.name}}AttributeGetter{% else %}nullptr{% endif %}, + {% if attribute.has_cross_origin_getter %} + {% if attribute.has_custom_getter %} + {{v8_class}}::{{attribute.name}}AttributeGetterCustom, + {% else %} + &{{cpp_class}}V8Internal::{{attribute.name}}AttributeGetter, + {% endif %} + {% else %} + nullptr, + {% endif %} {%+ if attribute.has_cross_origin_setter %}&{{cpp_class}}V8Internal::{{attribute.name}}AttributeSetter{% else %}nullptr{% endif %}, }, {% endfor %} @@ -246,11 +254,12 @@ const DOMWindow* targetWindow = V8Window::toImpl(window); return BindingSecurity::shouldAllowAccessTo(toLocalDOMWindow(toDOMWindow(accessingContext)), targetWindow, BindingSecurity::ErrorReportOption::DoNotReport); - {% else %}{# if interface_name == 'Window' #} - {# Not 'Window' means it\'s Location. #} + {% elif interface_name == 'Location' %} {{cpp_class}}* impl = {{v8_class}}::toImpl(accessedObject); return BindingSecurity::shouldAllowAccessTo(toLocalDOMWindow(toDOMWindow(accessingContext)), impl, BindingSecurity::ErrorReportOption::DoNotReport); - {% endif %}{# if interface_name == 'Window' #} + {% else %} + #error "Unexpected security check for interface {{interface_name}}" + {% endif %} } {% if has_cross_origin_named_getter %} @@ -309,9 +318,11 @@ for (const auto& attribute : {{cpp_class_or_partial}}V8Internal::kCrossOriginAttributeTable) names.push_back(attribute.name); - v8SetReturnValue( - info, - ToV8(names, info.Holder(), info.GetIsolate()).As<v8::Array>()); + // Use the current context as the creation context, as a cross-origin access + // may involve an object that does not have a creation context. + v8SetReturnValue(info, + ToV8(names, info.GetIsolate()->GetCurrentContext()->Global(), + info.GetIsolate()).As<v8::Array>()); } {% endif %}
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp index 6d7138f..5a28da3 100644 --- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp +++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp
@@ -396,8 +396,7 @@ } bool V8TestInterfaceCheckSecurity::securityCheck(v8::Local<v8::Context> accessingContext, v8::Local<v8::Object> accessedObject, v8::Local<v8::Value> data) { - TestInterfaceCheckSecurity* impl = V8TestInterfaceCheckSecurity::toImpl(accessedObject); - return BindingSecurity::shouldAllowAccessTo(toLocalDOMWindow(toDOMWindow(accessingContext)), impl, BindingSecurity::ErrorReportOption::DoNotReport); + #error "Unexpected security check for interface TestInterfaceCheckSecurity" } void V8TestInterfaceCheckSecurity::crossOriginNamedGetter(v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { @@ -439,9 +438,11 @@ for (const auto& attribute : TestInterfaceCheckSecurityV8Internal::kCrossOriginAttributeTable) names.push_back(attribute.name); - v8SetReturnValue( - info, - ToV8(names, info.Holder(), info.GetIsolate()).As<v8::Array>()); + // Use the current context as the creation context, as a cross-origin access + // may involve an object that does not have a creation context. + v8SetReturnValue(info, + ToV8(names, info.GetIsolate()->GetCurrentContext()->Global(), + info.GetIsolate()).As<v8::Array>()); } // Suppress warning: global constructors, because AttributeConfiguration is trivial
diff --git a/third_party/WebKit/Source/core/css/CSSDefaultStyleSheets.cpp b/third_party/WebKit/Source/core/css/CSSDefaultStyleSheets.cpp index b92e529..992b9a9 100644 --- a/third_party/WebKit/Source/core/css/CSSDefaultStyleSheets.cpp +++ b/third_party/WebKit/Source/core/css/CSSDefaultStyleSheets.cpp
@@ -34,6 +34,7 @@ #include "core/css/MediaQueryEvaluator.h" #include "core/css/RuleSet.h" #include "core/css/StyleSheetContents.h" +#include "core/dom/Fullscreen.h" #include "core/html/HTMLAnchorElement.h" #include "core/html/HTMLHtmlElement.h" #include "core/layout/LayoutTheme.h"
diff --git a/third_party/WebKit/Source/core/css/SelectorChecker.cpp b/third_party/WebKit/Source/core/css/SelectorChecker.cpp index b28a486..da7b0ec 100644 --- a/third_party/WebKit/Source/core/css/SelectorChecker.cpp +++ b/third_party/WebKit/Source/core/css/SelectorChecker.cpp
@@ -1005,7 +1005,7 @@ if (isHTMLFrameElementBase(element) && element.containsFullScreenElement()) return true; - return Fullscreen::isFullscreenElement(element); + return Fullscreen::isCurrentFullScreenElement(element); case CSSSelector::PseudoFullScreenAncestor: return element.containsFullScreenElement(); case CSSSelector::PseudoInRange:
diff --git a/third_party/WebKit/Source/core/dom/DocumentFullscreen.cpp b/third_party/WebKit/Source/core/dom/DocumentFullscreen.cpp index 2f5d0d7c..87d2577f 100644 --- a/third_party/WebKit/Source/core/dom/DocumentFullscreen.cpp +++ b/third_party/WebKit/Source/core/dom/DocumentFullscreen.cpp
@@ -41,4 +41,8 @@ Fullscreen::exitFullscreen(document); } +Element* DocumentFullscreen::currentFullScreenElement(Document& document) { + return Fullscreen::currentFullScreenElementForBindingFrom(document); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/DocumentFullscreen.h b/third_party/WebKit/Source/core/dom/DocumentFullscreen.h index 5443a42..aa8da741 100644 --- a/third_party/WebKit/Source/core/dom/DocumentFullscreen.h +++ b/third_party/WebKit/Source/core/dom/DocumentFullscreen.h
@@ -45,6 +45,9 @@ DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(fullscreenchange); DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(fullscreenerror); + // Mozilla version + static Element* currentFullScreenElement(Document&); + DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(webkitfullscreenchange); DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(webkitfullscreenerror); };
diff --git a/third_party/WebKit/Source/core/dom/DocumentFullscreen.idl b/third_party/WebKit/Source/core/dom/DocumentFullscreen.idl index 316ebb6..23225e7 100644 --- a/third_party/WebKit/Source/core/dom/DocumentFullscreen.idl +++ b/third_party/WebKit/Source/core/dom/DocumentFullscreen.idl
@@ -30,8 +30,8 @@ [RuntimeEnabled=FullscreenUnprefixed] attribute EventHandler onfullscreenerror; // Mozilla version - [MeasureAs=PrefixedDocumentIsFullscreen, ImplementedAs=fullscreenElement] readonly attribute boolean webkitIsFullScreen; - [MeasureAs=PrefixedDocumentCurrentFullScreenElement, ImplementedAs=fullscreenElement] readonly attribute Element webkitCurrentFullScreenElement; + [MeasureAs=PrefixedDocumentIsFullscreen, ImplementedAs=currentFullScreenElement] readonly attribute boolean webkitIsFullScreen; + [MeasureAs=PrefixedDocumentCurrentFullScreenElement, ImplementedAs=currentFullScreenElement] readonly attribute Element webkitCurrentFullScreenElement; [MeasureAs=PrefixedDocumentCancelFullScreen, ImplementedAs=exitFullscreen] void webkitCancelFullScreen(); // W3C version
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp index dde22c8..81d88538 100644 --- a/third_party/WebKit/Source/core/dom/Element.cpp +++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -1630,7 +1630,7 @@ DCHECK(!hasRareData() || !elementRareData()->hasPseudoElements()); - if (Fullscreen::isFullscreenElement(*this)) { + if (Fullscreen::isCurrentFullScreenElement(*this)) { setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false); if (insertionPoint->isElementNode()) { toElement(insertionPoint)->setContainsFullScreenElement(false); @@ -4047,7 +4047,7 @@ return false; if (hasAnimations()) return false; - if (Fullscreen::isFullscreenElement(*this)) + if (Fullscreen::isCurrentFullScreenElement(*this)) return false; return true; }
diff --git a/third_party/WebKit/Source/core/dom/Fullscreen.cpp b/third_party/WebKit/Source/core/dom/Fullscreen.cpp index 5fbb037d..4868177 100644 --- a/third_party/WebKit/Source/core/dom/Fullscreen.cpp +++ b/third_party/WebKit/Source/core/dom/Fullscreen.cpp
@@ -33,6 +33,7 @@ #include "core/dom/Document.h" #include "core/dom/ElementTraversal.h" #include "core/dom/StyleEngine.h" +#include "core/dom/TaskRunnerHelper.h" #include "core/events/Event.h" #include "core/frame/FrameView.h" #include "core/frame/HostsUsingFeatures.h" @@ -186,26 +187,17 @@ return true; } -// https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen step 4: -bool requestFullscreenConditionsMet(Element& pending, Document& document) { - // |pending|'s namespace is the HTML namespace or |pending| is an SVG svg or - // MathML math element. Note: MathML is not supported. - if (!pending.isHTMLElement() && !isSVGSVGElement(pending)) - return false; +bool isPrefixed(const AtomicString& type) { + return type == EventTypeNames::webkitfullscreenchange || + type == EventTypeNames::webkitfullscreenerror; +} - // The fullscreen element ready check for |pending| returns false. - if (!fullscreenElementReady(pending)) - return false; - - // Fullscreen is supported. - if (!fullscreenIsSupported(document)) - return false; - - // This algorithm is allowed to request fullscreen. - if (!allowedToRequestFullscreen(document)) - return false; - - return true; +Event* createEvent(const AtomicString& type, EventTarget& target) { + EventInit initializer; + initializer.setBubbles(isPrefixed(type)); + Event* event = Event::create(type, initializer); + event->setTarget(&target); + return event; } // Walks the frame tree and returns the first local ancestor frame, if any. @@ -224,7 +216,7 @@ LocalFrame* frame = document.frame(); if (!frame) return nullptr; - LocalFrame* next = nextLocalAncestor(*frame); + LocalFrame* next = nextLocalAncestor(*document.frame()); if (!next) return nullptr; DCHECK(next->document()); @@ -242,94 +234,34 @@ return document; } -// https://fullscreen.spec.whatwg.org/#collect-documents-to-unfullscreen -HeapVector<Member<Document>> collectDocumentsToUnfullscreen( - Document& doc, - Fullscreen::ExitType exitType) { - DCHECK(Fullscreen::fullscreenElementFrom(doc)); - - // 1. If |doc|'s top layer consists of more than a single element that has - // its fullscreen flag set, return the empty set. - // TODO(foolip): See TODO in |fullyExitFullscreen()|. - if (exitType != Fullscreen::ExitType::Fully && - Fullscreen::fullscreenElementStackSizeFrom(doc) > 1) - return HeapVector<Member<Document>>(); - - // 2. Let |docs| be an ordered set consisting of |doc|. - HeapVector<Member<Document>> docs; - docs.push_back(&doc); - - // 3. While |docs|'s last document ... - // - // OOPIF: Skip over remote frames, assuming that they have exactly one element - // in their fullscreen element stacks, thereby erring on the side of exiting - // fullscreen. TODO(alexmos): Deal with nested fullscreen cases, see - // https://crbug.com/617369. - for (Document* document = nextLocalAncestor(doc); document; - document = nextLocalAncestor(*document)) { - // ... has a browsing context container whose node document's top layer - // consists of a single element that has its fullscreen flag set and does - // not have its iframe fullscreen flag set (if any), append that node - // document to |docs|. - // TODO(foolip): Support the iframe fullscreen flag. - // https://crbug.com/644695 - if (Fullscreen::fullscreenElementStackSizeFrom(*document) == 1) - docs.push_back(document); - else - break; - } - - // 4. Return |docs|. - return docs; +// Helper to find the browsing context container in |doc| that embeds the +// |descendant| Document, possibly through multiple levels of nesting. This +// works even in OOPIF scenarios like A-B-A, where there may be remote frames +// in between |doc| and |descendant|. +HTMLFrameOwnerElement* findContainerForDescendant(const Document& doc, + const Document& descendant) { + Frame* frame = descendant.frame(); + while (frame->tree().parent() != doc.frame()) + frame = frame->tree().parent(); + return toHTMLFrameOwnerElement(frame->owner()); } -// Creates a non-bubbling event with |document| as its target. -Event* createEvent(const AtomicString& type, Document& document) { - DCHECK(type == EventTypeNames::fullscreenchange || - type == EventTypeNames::fullscreenerror); +// Fullscreen status affects scroll paint properties through +// FrameView::userInputScrollable(). +void setNeedsPaintPropertyUpdate(Document* document) { + if (!RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() || + RuntimeEnabledFeatures::rootLayerScrollingEnabled()) + return; - Event* event = Event::create(type); - event->setTarget(&document); - return event; -} + if (!document) + return; -// Creates a bubbling event with |element| as its target. If |element| is not -// connected to |document|, then |document| is used as the target instead. -Event* createPrefixedEvent(const AtomicString& type, - Element& element, - Document& document) { - DCHECK(type == EventTypeNames::webkitfullscreenchange || - type == EventTypeNames::webkitfullscreenerror); + LocalFrame* frame = document->frame(); + if (!frame) + return; - Event* event = Event::createBubble(type); - if (element.isConnected() && element.document() == document) - event->setTarget(&element); - else - event->setTarget(&document); - return event; -} - -Event* createChangeEvent(Document& document, - Element& element, - Fullscreen::RequestType requestType) { - if (requestType == Fullscreen::RequestType::Unprefixed) - return createEvent(EventTypeNames::fullscreenchange, document); - return createPrefixedEvent(EventTypeNames::webkitfullscreenchange, element, - document); -} - -Event* createErrorEvent(Document& document, - Element& element, - Fullscreen::RequestType requestType) { - if (requestType == Fullscreen::RequestType::Unprefixed) - return createEvent(EventTypeNames::fullscreenerror, document); - return createPrefixedEvent(EventTypeNames::webkitfullscreenerror, element, - document); -} - -void dispatchEvents(const HeapVector<Member<Event>>& events) { - for (Event* event : events) - event->target()->dispatchEvent(event); + if (FrameView* frameView = frame->view()) + frameView->setNeedsPaintPropertyUpdate(); } } // anonymous namespace @@ -359,12 +291,6 @@ return nullptr; } -size_t Fullscreen::fullscreenElementStackSizeFrom(Document& document) { - if (Fullscreen* found = fromIfExists(document)) - return found->m_fullscreenElementStack.size(); - return 0; -} - Element* Fullscreen::fullscreenElementForBindingFrom(TreeScope& scope) { Element* element = fullscreenElementFrom(scope.document()); if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled()) @@ -386,43 +312,66 @@ return scope.adjustedElement(*element); } +Element* Fullscreen::currentFullScreenElementFrom(Document& document) { + if (Fullscreen* found = fromIfExists(document)) + return found->currentFullScreenElement(); + return nullptr; +} + +Element* Fullscreen::currentFullScreenElementForBindingFrom( + Document& document) { + Element* element = currentFullScreenElementFrom(document); + if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled()) + return element; + + // For Shadow DOM V0 compatibility: We allow returning an element in V0 shadow + // tree, even though it leaks the Shadow DOM. + if (element->isInV0ShadowTree()) { + UseCounter::count(document, + UseCounter::DocumentFullscreenElementInV0Shadow); + return element; + } + return document.adjustedElement(*element); +} + Fullscreen::Fullscreen(Document& document) : Supplement<Document>(document), ContextLifecycleObserver(&document), - m_fullScreenLayoutObject(nullptr) { + m_fullScreenLayoutObject(nullptr), + m_eventQueueTimer(TaskRunnerHelper::get(TaskType::Unthrottled, &document), + this, + &Fullscreen::eventQueueTimerFired), + m_forCrossProcessDescendant(false) { document.setHasFullscreenSupplement(); } Fullscreen::~Fullscreen() {} -Document* Fullscreen::document() { +inline Document* Fullscreen::document() { return toDocument(lifecycleContext()); } void Fullscreen::contextDestroyed(ExecutionContext*) { + m_eventQueue.clear(); + if (m_fullScreenLayoutObject) m_fullScreenLayoutObject->destroy(); - m_pendingRequests.clear(); + m_currentFullScreenElement = nullptr; m_fullscreenElementStack.clear(); } // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen -void Fullscreen::requestFullscreen(Element& pending) { +void Fullscreen::requestFullscreen(Element& element) { // TODO(foolip): Make RequestType::Unprefixed the default when the unprefixed // API is enabled. https://crbug.com/383813 - requestFullscreen(pending, RequestType::Prefixed); + requestFullscreen(element, RequestType::Prefixed, false); } -void Fullscreen::requestFullscreen(Element& pending, RequestType requestType) { - Document& document = pending.document(); - - // Ignore this call if the document is not in a live frame. - if (!document.isActive() || !document.frame()) - return; - - bool forCrossProcessDescendant = - requestType == RequestType::PrefixedForCrossProcessDescendant; +void Fullscreen::requestFullscreen(Element& element, + RequestType requestType, + bool forCrossProcessDescendant) { + Document& document = element.document(); // Use counters only need to be incremented in the process of the actual // fullscreen element. @@ -436,326 +385,229 @@ } } - // 1. Let |pending| be the context object. - - // 2. Let |error| be false. - bool error = false; - - // 3. Let |promise| be a new promise. - // TODO(foolip): Promises. https://crbug.com/644637 - - // 4. If any of the following conditions are false, then set |error| to true: - // - // OOPIF: If |requestFullscreen()| was already called in a descendant frame - // and passed the checks, do not check again here. - if (!forCrossProcessDescendant && - !requestFullscreenConditionsMet(pending, document)) - error = true; - - // 5. Return |promise|, and run the remaining steps in parallel. - // TODO(foolip): Promises. https://crbug.com/644637 - - // 6. If |error| is false: Resize pending's top-level browsing context's - // document's viewport's dimensions to match the dimensions of the screen of - // the output device. Optionally display a message how the end user can - // revert this. - if (!error) { - from(document).m_pendingRequests.push_back( - std::make_pair(&pending, requestType)); - LocalFrame& frame = *document.frame(); - frame.chromeClient().enterFullscreen(frame); - } else { - enqueueTaskForRequest(document, pending, requestType, true); - } -} - -void Fullscreen::didEnterFullscreen() { - if (!document()) + // Ignore this call if the document is not in a live frame. + if (!document.isActive() || !document.frame()) return; - ElementStack requests; - requests.swap(m_pendingRequests); - for (const ElementStackEntry& request : requests) - enqueueTaskForRequest(*document(), *request.first, request.second, false); -} - -void Fullscreen::enqueueTaskForRequest(Document& document, - Element& pending, - RequestType requestType, - bool error) { - // 7. As part of the next animation frame task, run these substeps: - document.enqueueAnimationFrameTask( - WTF::bind(&runTaskForRequest, wrapPersistent(&document), - wrapPersistent(&pending), requestType, error)); -} - -void Fullscreen::runTaskForRequest(Document* document, - Element* element, - RequestType requestType, - bool error) { - DCHECK(document); - DCHECK(document->isActive()); - DCHECK(document->frame()); - DCHECK(element); - - Document& pendingDoc = *document; - Element& pending = *element; - - // TODO(foolip): Spec something like: If |pending|'s node document is not - // |pendingDoc|, then set |error| to true. - // https://github.com/whatwg/fullscreen/issues/33 - if (pending.document() != pendingDoc) - error = true; - - // 7.1. If either |error| is true or the fullscreen element ready check for - // |pending| returns false, fire an event named fullscreenerror on - // |pending|'s node document, reject |promise| with a TypeError exception, - // and terminate these steps. - // TODO(foolip): Promises. https://crbug.com/644637 - if (error || !fullscreenElementReady(pending)) { - Event* event = createErrorEvent(pendingDoc, pending, requestType); - event->target()->dispatchEvent(event); + // If |element| is on top of |doc|'s fullscreen element stack, terminate these + // substeps. + if (&element == fullscreenElementFrom(document)) return; - } - // 7.2. Let |fullscreenElements| be an ordered set initially consisting of - // |pending|. - HeapDeque<Member<Element>> fullscreenElements; - fullscreenElements.append(pending); + do { + // 1. If any of the following conditions are false, then terminate these + // steps and queue a task to fire an event named fullscreenerror with its + // bubbles attribute set to true on the context object's node document: - // 7.3. While the first element in |fullscreenElements| is in a nested - // browsing context, prepend its browsing context container to - // |fullscreenElements|. - // - // OOPIF: |fullscreenElements| will only contain elements for local ancestors, - // and remote ancestors will be processed in their respective processes. This - // preserves the spec's event firing order for local ancestors, but not for - // remote ancestors. However, that difference shouldn't be observable in - // practice: a fullscreenchange event handler would need to postMessage a - // frame in another renderer process, where the message should be queued up - // and processed after the IPC that dispatches fullscreenchange. - for (Frame* frame = pending.document().frame(); frame; - frame = frame->tree().parent()) { - if (!frame->owner() || !frame->owner()->isLocal()) - continue; - Element* element = toHTMLFrameOwnerElement(frame->owner()); - fullscreenElements.prepend(element); - } + // |element|'s namespace is the HTML namespace or |element| is an SVG + // svg or MathML math element. + // Note: MathML is not supported. + if (!element.isHTMLElement() && !isSVGSVGElement(element)) + break; - // 7.4. Let |eventDocs| be an empty list. - // Note: For prefixed requests, the event target is an element, so instead - // let |events| be a list of events to dispatch. - HeapVector<Member<Event>> events; + // The fullscreen element ready check for |element| returns true. + if (!fullscreenElementReady(element)) + break; - // 7.5. For each |element| in |fullscreenElements|, in order, run these - // subsubsteps: - for (Element* element : fullscreenElements) { - // 7.5.1. Let |doc| be |element|'s node document. - Document& doc = element->document(); + // Fullscreen is supported. + if (!fullscreenIsSupported(document)) + break; - // 7.5.2. If |element| is |doc|'s fullscreen element, terminate these - // subsubsteps. - if (element == fullscreenElementFrom(doc)) - continue; + // This algorithm is allowed to request fullscreen. + // OOPIF: If |forCrossProcessDescendant| is true, requestFullscreen was + // already called on a descendant element in another process, and + // getting here means that it was already allowed to request fullscreen. + if (!forCrossProcessDescendant && !allowedToRequestFullscreen(document)) + break; - // 7.5.3. Otherwise, append |doc| to |eventDocs|. - events.push_back(createChangeEvent(doc, *element, requestType)); + // 2. Let doc be element's node document. (i.e. "this") - // 7.5.4. If |element| is |pending| and |pending| is an iframe element, - // set |element|'s iframe fullscreen flag. - // TODO(foolip): Support the iframe fullscreen flag. - // https://crbug.com/644695 + // 3. Let docs be all doc's ancestor browsing context's documents (if any) + // and doc. + // + // For OOPIF scenarios, |docs| will only contain documents for local + // ancestors, and remote ancestors will be processed in their + // respective processes. This preserves the spec's event firing order + // for local ancestors, but not for remote ancestors. However, that + // difference shouldn't be observable in practice: a fullscreenchange + // event handler would need to postMessage a frame in another renderer + // process, where the message should be queued up and processed after + // the IPC that dispatches fullscreenchange. + HeapDeque<Member<Document>> docs; + for (Document* doc = &document; doc; doc = nextLocalAncestor(*doc)) + docs.prepend(doc); - // 7.5.5. Fullscreen |element| within |doc|. - // TODO(foolip): Merge fullscreen element stack into top layer. - // https://crbug.com/627790 - from(doc).pushFullscreenElementStack(*element, requestType); - } + // 4. For each document in docs, run these substeps: + HeapDeque<Member<Document>>::iterator current = docs.begin(), + following = docs.begin(); - // 7.6. For each |doc| in |eventDocs|, in order, fire an event named - // fullscreenchange on |doc|. - dispatchEvents(events); + do { + ++following; - // 7.7. Fulfill |promise| with undefined. - // TODO(foolip): Promises. https://crbug.com/644637 + // 1. Let following document be the document after document in docs, or + // null if there is no such document. + Document* currentDoc = *current; + Document* followingDoc = following != docs.end() ? *following : nullptr; + + // 2. If following document is null, push context object on document's + // fullscreen element stack, and queue a task to fire an event named + // fullscreenchange with its bubbles attribute set to true on the + // document. + if (!followingDoc) { + from(*currentDoc).pushFullscreenElementStack(element, requestType); + from(document).enqueueChangeEvent(*currentDoc, requestType); + continue; + } + + // 3. Otherwise, if document's fullscreen element stack is either empty or + // its top element is not following document's browsing context container, + Element* topElement = fullscreenElementFrom(*currentDoc); + HTMLFrameOwnerElement* followingOwner = + findContainerForDescendant(*currentDoc, *followingDoc); + if (!topElement || topElement != followingOwner) { + // ...push following document's browsing context container on document's + // fullscreen element stack, and queue a task to fire an event named + // fullscreenchange with its bubbles attribute set to true on document. + from(*currentDoc) + .pushFullscreenElementStack(*followingOwner, requestType); + from(document).enqueueChangeEvent(*currentDoc, requestType); + continue; + } + + // 4. Otherwise, do nothing for this document. It stays the same. + } while (++current != docs.end()); + + from(document).m_forCrossProcessDescendant = forCrossProcessDescendant; + + // 5. Return, and run the remaining steps asynchronously. + // 6. Optionally, perform some animation. + from(document).m_pendingFullscreenElement = &element; + document.frame()->chromeClient().enterFullscreen(*document.frame()); + + // 7. Optionally, display a message indicating how the user can exit + // displaying the context object fullscreen. + return; + } while (false); + + from(document).enqueueErrorEvent(element, requestType); } // https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen void Fullscreen::fullyExitFullscreen(Document& document) { - // 1. If |document|'s fullscreen element is null, terminate these steps. + // To fully exit fullscreen, run these steps: - // 2. Unfullscreen elements whose fullscreen flag is set, within - // |document|'s top layer, except for |document|'s fullscreen element. + // 1. Let |doc| be the top-level browsing context's document. + // + // Since the top-level browsing context's document might be unavailable in + // OOPIF scenarios (i.e., when the top frame is remote), this actually uses + // the Document of the topmost local ancestor frame. Without OOPIF, this + // will be the top frame's document. With OOPIF, each renderer process for + // the current page will separately call fullyExitFullscreen to cover all + // local frames in each process. + Document& doc = topmostLocalAncestor(document); - // 3. Exit fullscreen |document|. + // 2. If |doc|'s fullscreen element stack is empty, terminate these steps. + if (!fullscreenElementFrom(doc)) + return; - // TODO(foolip): Change the spec. To remove elements from |document|'s top - // layer as in step 2 could leave descendant frames in fullscreen. It may work - // to give the "exit fullscreen" algorithm a |fully| flag that's used in the - // animation frame task after exit. Here, retain the old behavior of fully - // exiting fullscreen for the topmost local ancestor: - exitFullscreen(topmostLocalAncestor(document), ExitType::Fully); + // 3. Remove elements from |doc|'s fullscreen element stack until only the top + // element is left. + size_t stackSize = from(doc).m_fullscreenElementStack.size(); + from(doc).m_fullscreenElementStack.remove(0, stackSize - 1); + DCHECK_EQ(from(doc).m_fullscreenElementStack.size(), 1u); + + // 4. Act as if the exitFullscreen() method was invoked on |doc|. + exitFullscreen(doc); } // https://fullscreen.spec.whatwg.org/#exit-fullscreen -void Fullscreen::exitFullscreen(Document& doc, ExitType exitType) { - if (!doc.isActive() || !doc.frame()) +void Fullscreen::exitFullscreen(Document& document) { + // The exitFullscreen() method must run these steps: + + // Ignore this call if the document is not in a live frame. + if (!document.isActive() || !document.frame()) return; - // 1. Let |promise| be a new promise. - // 2. If |doc|'s fullscreen element is null, reject |promise| with a - // TypeError exception, and return |promise|. - // TODO(foolip): Promises. https://crbug.com/644637 - if (!fullscreenElementFrom(doc)) + // 1. Let doc be the context object. (i.e. "this") + // 2. If doc's fullscreen element stack is empty, terminate these steps. + if (!fullscreenElementFrom(document)) return; - // 3. Let |resize| be false. - bool resize = false; - - // 4. Let |docs| be the result of collecting documents to unfullscreen given - // |doc|. - HeapVector<Member<Document>> docs = - collectDocumentsToUnfullscreen(doc, exitType); - - // 5. Let |topLevelDoc| be |doc|'s top-level browsing context's document. - // - // OOPIF: Let |topLevelDoc| be the topmost local ancestor instead. If the main - // frame is in another process, we will still fully exit fullscreen even - // though that's wrong if the main frame was in nested fullscreen. - // TODO(alexmos): Deal with nested fullscreen cases, see - // https://crbug.com/617369. - Document& topLevelDoc = topmostLocalAncestor(doc); - - // 6. If |topLevelDoc| is in |docs|, set |resize| to true. - if (!docs.isEmpty() && docs.back() == &topLevelDoc) - resize = true; - - // 7. Return |promise|, and run the remaining steps in parallel. - // TODO(foolip): Promises. https://crbug.com/644637 - - // Note: |ExitType::Fully| is only used together with the topmost local - // ancestor in |fullyExitFullscreen()|, and so implies that |resize| is true. - // This would change if matching the spec for "fully exit fullscreen". - if (exitType == ExitType::Fully) - DCHECK(resize); - - // 8. If |resize| is true, resize |topLevelDoc|'s viewport to its "normal" - // dimensions. - if (resize) { - LocalFrame& frame = *doc.frame(); - frame.chromeClient().exitFullscreen(frame); - } else { - enqueueTaskForExit(doc, exitType); - } -} - -void Fullscreen::didExitFullscreen() { - if (!document()) - return; - - DCHECK_EQ(document(), &topmostLocalAncestor(*document())); - - enqueueTaskForExit(*document(), ExitType::Fully); -} - -void Fullscreen::enqueueTaskForExit(Document& document, ExitType exitType) { - // 9. As part of the next animation frame task, run these substeps: - document.enqueueAnimationFrameTask( - WTF::bind(&runTaskForExit, wrapPersistent(&document), exitType)); -} - -void Fullscreen::runTaskForExit(Document* document, ExitType exitType) { - DCHECK(document); - DCHECK(document->isActive()); - DCHECK(document->frame()); - - Document& doc = *document; - - if (!fullscreenElementFrom(doc)) - return; - - // 9.1. Let |exitDocs| be the result of collecting documents to unfullscreen - // given |doc|. - - // 9.2. If |resize| is true and |topLevelDoc| is not in |exitDocs|, fully - // exit fullscreen |topLevelDoc|, reject promise with a TypeError exception, - // and terminate these steps. - - // TODO(foolip): See TODO in |fullyExitFullscreen()|. Instead of using "fully - // exit fullscreen" in step 9.2 (which is async), give "exit fullscreen" a - // |fully| flag which is always true if |resize| was true. - - HeapVector<Member<Document>> exitDocs = - collectDocumentsToUnfullscreen(doc, exitType); - - // 9.3. If |exitDocs| is the empty set, append |doc| to |exitDocs|. - if (exitDocs.isEmpty()) - exitDocs.push_back(&doc); - - // 9.4. If |exitDocs|'s last document has a browsing context container, - // append that browsing context container's node document to |exitDocs|. - // - // OOPIF: Skip over remote frames, assuming that they have exactly one element - // in their fullscreen element stacks, thereby erring on the side of exiting - // fullscreen. TODO(alexmos): Deal with nested fullscreen cases, see - // https://crbug.com/617369. - if (Document* document = nextLocalAncestor(*exitDocs.back())) - exitDocs.push_back(document); - - // 9.5. Let |descendantDocs| be an ordered set consisting of |doc|'s - // descendant browsing contexts' documents whose fullscreen element is - // non-null, if any, in *reverse* tree order. - HeapDeque<Member<Document>> descendantDocs; - for (Frame* descendant = doc.frame()->tree().firstChild(); descendant; - descendant = descendant->tree().traverseNext(doc.frame())) { + // 3. Let descendants be all the doc's descendant browsing context's documents + // with a non-empty fullscreen element stack (if any), ordered so that the + // child of the doc is last and the document furthest away from the doc is + // first. + HeapDeque<Member<Document>> descendants; + for (Frame* descendant = document.frame()->tree().traverseNext(); descendant; + descendant = descendant->tree().traverseNext()) { if (!descendant->isLocalFrame()) continue; DCHECK(toLocalFrame(descendant)->document()); if (fullscreenElementFrom(*toLocalFrame(descendant)->document())) - descendantDocs.prepend(toLocalFrame(descendant)->document()); + descendants.prepend(toLocalFrame(descendant)->document()); } - // Note: For prefixed requests, the event target is an element, so let - // |events| be a list of events to dispatch. - HeapVector<Member<Event>> events; - - // 9.6. For each |descendantDoc| in |descendantDocs|, in order, unfullscreen - // |descendantDoc|. - for (Document* descendantDoc : descendantDocs) { - Fullscreen& fullscreen = from(*descendantDoc); - ElementStack& stack = fullscreen.m_fullscreenElementStack; - DCHECK(!stack.isEmpty()); - events.push_back(createChangeEvent(*descendantDoc, *stack.back().first, - stack.back().second)); - while (!stack.isEmpty()) - fullscreen.popFullscreenElementStack(); + // 4. For each descendant in descendants, empty descendant's fullscreen + // element stack, and queue a task to fire an event named fullscreenchange + // with its bubbles attribute set to true on descendant. + for (auto& descendant : descendants) { + DCHECK(descendant); + RequestType requestType = + from(*descendant).m_fullscreenElementStack.back().second; + from(*descendant).clearFullscreenElementStack(); + from(document).enqueueChangeEvent(*descendant, requestType); } - // 9.7. For each |exitDoc| in |exitDocs|, in order, unfullscreen |exitDoc|'s - // fullscreen element. - for (Document* exitDoc : exitDocs) { - Fullscreen& fullscreen = from(*exitDoc); - ElementStack& stack = fullscreen.m_fullscreenElementStack; - DCHECK(!stack.isEmpty()); - events.push_back( - createChangeEvent(*exitDoc, *stack.back().first, stack.back().second)); - fullscreen.popFullscreenElementStack(); + // 5. While doc is not null, run these substeps: + Element* newTop = nullptr; + for (Document* currentDoc = &document; currentDoc;) { + RequestType requestType = + from(*currentDoc).m_fullscreenElementStack.back().second; - // TODO(foolip): See TODO in |fullyExitFullscreen()|. - if (exitDoc == &doc && exitType == ExitType::Fully) { - while (!stack.isEmpty()) - fullscreen.popFullscreenElementStack(); + // 1. Pop the top element of doc's fullscreen element stack. + from(*currentDoc).popFullscreenElementStack(); + + // If doc's fullscreen element stack is non-empty and the element now at + // the top is either not in a document or its node document is not doc, + // repeat this substep. + newTop = fullscreenElementFrom(*currentDoc); + if (newTop && (!newTop->isConnected() || newTop->document() != currentDoc)) + continue; + + // 2. Queue a task to fire an event named fullscreenchange with its bubbles + // attribute set to true on doc. + from(document).enqueueChangeEvent(*currentDoc, requestType); + + // 3. If doc's fullscreen element stack is empty and doc's browsing context + // has a browsing context container, set doc to that browsing context + // container's node document. + // + // OOPIF: If browsing context container's document is in another + // process, keep moving up the ancestor chain and looking for a + // browsing context container with a local document. + // TODO(alexmos): Deal with nested fullscreen cases, see + // https://crbug.com/617369. + if (!newTop) { + currentDoc = nextLocalAncestor(*currentDoc); + continue; } + + // 4. Otherwise, set doc to null. + currentDoc = nullptr; } - // 9.8. For each |descendantDoc| in |descendantDocs|, in order, fire an - // event named fullscreenchange on |descendantDoc|. - // 9.9. For each |exitDoc| in |exitDocs|, in order, fire an event named - // fullscreenchange on |exitDoc|. - dispatchEvents(events); + // 6. Return, and run the remaining steps asynchronously. + // 7. Optionally, perform some animation. - // 9.10. Fulfill |promise| with undefined. - // TODO(foolip): Promises. https://crbug.com/644637 + // Only exit fullscreen mode if the fullscreen element stack is empty. + if (!newTop) { + document.frame()->chromeClient().exitFullscreen(*document.frame()); + return; + } + + // Otherwise, enter fullscreen for the fullscreen element stack's top element. + from(document).m_pendingFullscreenElement = newTop; + from(document).didEnterFullscreen(); } // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled @@ -767,6 +619,124 @@ fullscreenIsSupported(document); } +void Fullscreen::didEnterFullscreen() { + if (!document()->isActive() || !document()->frame()) + return; + + // Start the timer for events enqueued by |requestFullscreen()|. The hover + // state update is scheduled first so that it's done when the events fire. + document()->frame()->eventHandler().scheduleHoverStateUpdate(); + m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE); + + Element* element = m_pendingFullscreenElement.release(); + if (!element) + return; + + if (m_currentFullScreenElement == element) + return; + + if (!element->isConnected() || &element->document() != document()) { + // The element was removed or has moved to another document since the + // |requestFullscreen()| call. Exit fullscreen again to recover. + // TODO(foolip): Fire a fullscreenerror event. This is currently difficult + // because the fullscreenchange event has already been enqueued and possibly + // even fired. https://crbug.com/402376 + LocalFrame& frame = *document()->frame(); + frame.chromeClient().exitFullscreen(frame); + return; + } + + if (m_fullScreenLayoutObject) + m_fullScreenLayoutObject->unwrapLayoutObject(); + + Element* previousElement = m_currentFullScreenElement; + m_currentFullScreenElement = element; + + // Create a placeholder block for a the full-screen element, to keep the page + // from reflowing when the element is removed from the normal flow. Only do + // this for a LayoutBox, as only a box will have a frameRect. The placeholder + // will be created in setFullScreenLayoutObject() during layout. + LayoutObject* layoutObject = m_currentFullScreenElement->layoutObject(); + bool shouldCreatePlaceholder = layoutObject && layoutObject->isBox(); + if (shouldCreatePlaceholder) { + m_savedPlaceholderFrameRect = toLayoutBox(layoutObject)->frameRect(); + m_savedPlaceholderComputedStyle = + ComputedStyle::clone(layoutObject->styleRef()); + } + + // TODO(alexmos): When |m_forCrossProcessDescendant| is true, some of + // this layout work has already been done in another process, so it should + // not be necessary to repeat it here. + if (m_currentFullScreenElement != document()->documentElement()) { + LayoutFullScreen::wrapLayoutObject( + layoutObject, layoutObject ? layoutObject->parent() : 0, document()); + } + + // When |m_forCrossProcessDescendant| is true, m_currentFullScreenElement + // corresponds to the HTMLFrameOwnerElement for the out-of-process iframe + // that contains the actual fullscreen element. Hence, it must also set + // the ContainsFullScreenElement flag (so that it gains the + // -webkit-full-screen-ancestor style). + if (m_forCrossProcessDescendant) { + DCHECK(m_currentFullScreenElement->isFrameOwnerElement()); + DCHECK(toHTMLFrameOwnerElement(m_currentFullScreenElement) + ->contentFrame() + ->isRemoteFrame()); + m_currentFullScreenElement->setContainsFullScreenElement(true); + } + + m_currentFullScreenElement + ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true); + + document()->styleEngine().ensureUAStyleForFullscreen(); + m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen); + + // FIXME: This should not call updateStyleAndLayoutTree. + document()->updateStyleAndLayoutTree(); + + document()->frame()->chromeClient().fullscreenElementChanged(previousElement, + element); +} + +void Fullscreen::didExitFullscreen() { + if (!document()->isActive() || !document()->frame()) + return; + + // Start the timer for events enqueued by |exitFullscreen()|. The hover state + // update is scheduled first so that it's done when the events fire. + document()->frame()->eventHandler().scheduleHoverStateUpdate(); + m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE); + + // If fullscreen was canceled by the browser, e.g. if the user pressed Esc, + // then |exitFullscreen()| was never called. Let |fullyExitFullscreen()| clear + // the fullscreen element stack and fire any events as necessary. + // TODO(foolip): Remove this when state changes and events are synchronized + // with animation frames. https://crbug.com/402376 + fullyExitFullscreen(*document()); + + if (!m_currentFullScreenElement) + return; + + if (m_forCrossProcessDescendant) + m_currentFullScreenElement->setContainsFullScreenElement(false); + + m_currentFullScreenElement + ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false); + + if (m_fullScreenLayoutObject) + LayoutFullScreenItem(m_fullScreenLayoutObject).unwrapLayoutObject(); + + document()->styleEngine().ensureUAStyleForFullscreen(); + m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen); + Element* previousElement = m_currentFullScreenElement; + m_currentFullScreenElement = nullptr; + + m_forCrossProcessDescendant = false; + + document()->frame()->chromeClient().fullscreenElementChanged(previousElement, + nullptr); +} + void Fullscreen::setFullScreenLayoutObject(LayoutFullScreen* layoutObject) { if (layoutObject == m_fullScreenLayoutObject) return; @@ -793,9 +763,56 @@ m_fullScreenLayoutObject = nullptr; } -void Fullscreen::elementRemoved(Element& oldNode) { - DCHECK_EQ(document(), &oldNode.document()); +void Fullscreen::enqueueChangeEvent(Document& document, + RequestType requestType) { + Event* event; + if (requestType == RequestType::Unprefixed) { + event = createEvent(EventTypeNames::fullscreenchange, document); + } else { + DCHECK(document.hasFullscreenSupplement()); + Fullscreen& fullscreen = from(document); + EventTarget* target = fullscreen.fullscreenElement(); + if (!target) + target = fullscreen.currentFullScreenElement(); + if (!target) + target = &document; + event = createEvent(EventTypeNames::webkitfullscreenchange, *target); + } + m_eventQueue.append(event); + // NOTE: The timer is started in didEnterFullscreen/didExitFullscreen. +} +void Fullscreen::enqueueErrorEvent(Element& element, RequestType requestType) { + Event* event; + if (requestType == RequestType::Unprefixed) + event = createEvent(EventTypeNames::fullscreenerror, element.document()); + else + event = createEvent(EventTypeNames::webkitfullscreenerror, element); + m_eventQueue.append(event); + m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE); +} + +void Fullscreen::eventQueueTimerFired(TimerBase*) { + HeapDeque<Member<Event>> eventQueue; + m_eventQueue.swap(eventQueue); + + while (!eventQueue.isEmpty()) { + Event* event = eventQueue.takeFirst(); + Node* target = event->target()->toNode(); + + // If the element was removed from our tree, also message the + // documentElement. + if (!target->isConnected() && document()->documentElement()) { + DCHECK(isPrefixed(event->type())); + eventQueue.append( + createEvent(event->type(), *document()->documentElement())); + } + + target->dispatchEvent(event); + } +} + +void Fullscreen::elementRemoved(Element& oldNode) { // Whenever the removing steps run with an |oldNode| and |oldNode| is in its // node document's fullscreen element stack, run these steps: @@ -818,106 +835,36 @@ // NOTE: |oldNode| was not in the fullscreen element stack. } -void Fullscreen::popFullscreenElementStack() { - DCHECK(!m_fullscreenElementStack.isEmpty()); +void Fullscreen::clearFullscreenElementStack() { + if (m_fullscreenElementStack.isEmpty()) + return; - Element* previousElement = fullscreenElement(); + m_fullscreenElementStack.clear(); + + setNeedsPaintPropertyUpdate(document()); +} + +void Fullscreen::popFullscreenElementStack() { + if (m_fullscreenElementStack.isEmpty()) + return; + m_fullscreenElementStack.pop_back(); - // Note: |requestType| is only used if |fullscreenElement()| is non-null. - RequestType requestType = m_fullscreenElementStack.isEmpty() - ? RequestType::Unprefixed - : m_fullscreenElementStack.back().second; - fullscreenElementChanged(previousElement, fullscreenElement(), requestType); + setNeedsPaintPropertyUpdate(document()); } void Fullscreen::pushFullscreenElementStack(Element& element, RequestType requestType) { - Element* previousElement = fullscreenElement(); m_fullscreenElementStack.push_back(std::make_pair(&element, requestType)); - fullscreenElementChanged(previousElement, &element, requestType); -} - -void Fullscreen::fullscreenElementChanged(Element* fromElement, - Element* toElement, - RequestType toRequestType) { - DCHECK_NE(fromElement, toElement); - - if (!document()) - return; - - document()->styleEngine().ensureUAStyleForFullscreen(); - - if (m_fullScreenLayoutObject) - m_fullScreenLayoutObject->unwrapLayoutObject(); - DCHECK(!m_fullScreenLayoutObject); - - if (fromElement) { - DCHECK_NE(fromElement, fullscreenElement()); - - fromElement->pseudoStateChanged(CSSSelector::PseudoFullScreen); - - fromElement->setContainsFullScreenElement(false); - fromElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries( - false); - } - - if (toElement) { - DCHECK_EQ(toElement, fullscreenElement()); - - toElement->pseudoStateChanged(CSSSelector::PseudoFullScreen); - - // OOPIF: For RequestType::PrefixedForCrossProcessDescendant, |toElement| is - // the iframe element for the out-of-process frame that contains the - // fullscreen element. Hence, it must match :-webkit-full-screen-ancestor. - if (toRequestType == RequestType::PrefixedForCrossProcessDescendant) { - DCHECK(isHTMLIFrameElement(toElement)); - toElement->setContainsFullScreenElement(true); - } - toElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries( - true); - - // Create a placeholder block for the fullscreen element, to keep the page - // from reflowing when the element is removed from the normal flow. Only do - // this for a LayoutBox, as only a box will have a frameRect. The - // placeholder will be created in setFullScreenLayoutObject() during layout. - LayoutObject* layoutObject = toElement->layoutObject(); - bool shouldCreatePlaceholder = layoutObject && layoutObject->isBox(); - if (shouldCreatePlaceholder) { - m_savedPlaceholderFrameRect = toLayoutBox(layoutObject)->frameRect(); - m_savedPlaceholderComputedStyle = - ComputedStyle::clone(layoutObject->styleRef()); - } - - if (toElement != document()->documentElement()) { - LayoutFullScreen::wrapLayoutObject( - layoutObject, layoutObject ? layoutObject->parent() : 0, document()); - } - } - - if (LocalFrame* frame = document()->frame()) { - // TODO(foolip): Synchronize hover state changes with animation frames. - // https://crbug.com/668758 - frame->eventHandler().scheduleHoverStateUpdate(); - frame->chromeClient().fullscreenElementChanged(fromElement, toElement); - - if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() && - !RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { - // Fullscreen status affects scroll paint properties through - // FrameView::userInputScrollable(). - if (FrameView* frameView = frame->view()) - frameView->setNeedsPaintPropertyUpdate(); - } - } - - // TODO(foolip): This should not call updateStyleAndLayoutTree. - document()->updateStyleAndLayoutTree(); + setNeedsPaintPropertyUpdate(document()); } DEFINE_TRACE(Fullscreen) { - visitor->trace(m_pendingRequests); + visitor->trace(m_pendingFullscreenElement); visitor->trace(m_fullscreenElementStack); + visitor->trace(m_currentFullScreenElement); + visitor->trace(m_eventQueue); Supplement<Document>::trace(visitor); ContextLifecycleObserver::trace(visitor); }
diff --git a/third_party/WebKit/Source/core/dom/Fullscreen.h b/third_party/WebKit/Source/core/dom/Fullscreen.h index 3cdc8331..36f961a 100644 --- a/third_party/WebKit/Source/core/dom/Fullscreen.h +++ b/third_party/WebKit/Source/core/dom/Fullscreen.h
@@ -35,6 +35,7 @@ #include "core/dom/Document.h" #include "core/dom/Element.h" #include "platform/Supplementable.h" +#include "platform/Timer.h" #include "platform/geometry/LayoutRect.h" #include "wtf/Deque.h" #include "wtf/RefPtr.h" @@ -58,8 +59,9 @@ static Fullscreen* fromIfExists(Document&); static Element* fullscreenElementFrom(Document&); static Element* fullscreenElementForBindingFrom(TreeScope&); - static size_t fullscreenElementStackSizeFrom(Document&); - static bool isFullscreenElement(const Element&); + static Element* currentFullScreenElementFrom(Document&); + static Element* currentFullScreenElementForBindingFrom(Document&); + static bool isCurrentFullScreenElement(const Element&); enum class RequestType { // Element.requestFullscreen() @@ -67,26 +69,25 @@ // Element.webkitRequestFullscreen()/webkitRequestFullScreen() and // HTMLVideoElement.webkitEnterFullscreen()/webkitEnterFullScreen() Prefixed, - // For WebRemoteFrameImpl to notify that a cross-process descendant frame - // has requested and is about to enter fullscreen. - PrefixedForCrossProcessDescendant, }; static void requestFullscreen(Element&); - static void requestFullscreen(Element&, RequestType); + + // |forCrossProcessDescendant| is used in OOPIF scenarios and is set to + // true when fullscreen is requested for an out-of-process descendant + // element. + static void requestFullscreen(Element&, + RequestType, + bool forCrossProcessDescendant = false); static void fullyExitFullscreen(Document&); - - enum class ExitType { - // Exits fullscreen for one element in the document. - Default, - // Fully exits fullscreen for the document. - Fully, - }; - - static void exitFullscreen(Document&, ExitType = ExitType::Default); + static void exitFullscreen(Document&); static bool fullscreenEnabled(Document&); + // TODO(foolip): The fullscreen element stack is modified synchronously in + // requestFullscreen(), which is not per spec and means that + // |fullscreenElement()| is not always the same as + // |currentFullScreenElement()|, see https://crbug.com/402421. Element* fullscreenElement() const { return !m_fullscreenElementStack.isEmpty() ? m_fullscreenElementStack.back().first.get() @@ -106,6 +107,20 @@ void elementRemoved(Element&); + // Returns true if the current fullscreen element stack corresponds to a + // container for an actual fullscreen element in a descendant + // out-of-process iframe. + bool forCrossProcessDescendant() { return m_forCrossProcessDescendant; } + + // Mozilla API + // TODO(foolip): |currentFullScreenElement()| is a remnant from before the + // fullscreen element stack. It is still maintained separately from the + // stack and is is what the :-webkit-full-screen pseudo-class depends on. It + // should be removed, see https://crbug.com/402421. + Element* currentFullScreenElement() const { + return m_currentFullScreenElement.get(); + } + // ContextLifecycleObserver: void contextDestroyed(ExecutionContext*) override; @@ -118,30 +133,33 @@ Document* document(); - static void enqueueTaskForRequest(Document&, - Element&, - RequestType, - bool error); - static void runTaskForRequest(Document*, Element*, RequestType, bool error); - - static void enqueueTaskForExit(Document&, ExitType); - static void runTaskForExit(Document*, ExitType); - void clearFullscreenElementStack(); void popFullscreenElementStack(); void pushFullscreenElementStack(Element&, RequestType); - void fullscreenElementChanged(Element* fromElement, - Element* toElement, - RequestType toRequestType); - using ElementStackEntry = std::pair<Member<Element>, RequestType>; - using ElementStack = HeapVector<ElementStackEntry>; - ElementStack m_pendingRequests; - ElementStack m_fullscreenElementStack; + void enqueueChangeEvent(Document&, RequestType); + void enqueueErrorEvent(Element&, RequestType); + void eventQueueTimerFired(TimerBase*); + Member<Element> m_pendingFullscreenElement; + HeapVector<std::pair<Member<Element>, RequestType>> m_fullscreenElementStack; + Member<Element> m_currentFullScreenElement; LayoutFullScreen* m_fullScreenLayoutObject; + TaskRunnerTimer<Fullscreen> m_eventQueueTimer; + HeapDeque<Member<Event>> m_eventQueue; LayoutRect m_savedPlaceholderFrameRect; RefPtr<ComputedStyle> m_savedPlaceholderComputedStyle; + + // TODO(alexmos, dcheng): Currently, this assumes that if fullscreen was + // entered for an element in an out-of-process iframe, then it's not + // possible to re-enter fullscreen for a different element in this + // document, since that requires a user gesture, which can't be obtained + // since nothing in this document is visible, and since user gestures can't + // be forwarded across processes. However, the latter assumption could + // change if https://crbug.com/161068 is fixed so that cross-process + // postMessage can carry user gestures. If that happens, this should be + // moved to be part of |m_fullscreenElementStack|. + bool m_forCrossProcessDescendant; }; inline Fullscreen* Fullscreen::fromIfExists(Document& document) { @@ -150,9 +168,9 @@ return fromIfExistsSlow(document); } -inline bool Fullscreen::isFullscreenElement(const Element& element) { +inline bool Fullscreen::isCurrentFullScreenElement(const Element& element) { if (Fullscreen* found = fromIfExists(element.document())) - return found->fullscreenElement() == &element; + return found->currentFullScreenElement() == &element; return false; }
diff --git a/third_party/WebKit/Source/core/dom/LayoutTreeBuilder.cpp b/third_party/WebKit/Source/core/dom/LayoutTreeBuilder.cpp index 5b16dac..77046c3 100644 --- a/third_party/WebKit/Source/core/dom/LayoutTreeBuilder.cpp +++ b/third_party/WebKit/Source/core/dom/LayoutTreeBuilder.cpp
@@ -143,7 +143,7 @@ newLayoutObject->setStyle( &style); // setStyle() can depend on layoutObject() already being set. - if (Fullscreen::isFullscreenElement(*m_node)) { + if (Fullscreen::isCurrentFullScreenElement(*m_node)) { newLayoutObject = LayoutFullScreen::wrapLayoutObject( newLayoutObject, parentLayoutObject, &m_node->document()); if (!newLayoutObject)
diff --git a/third_party/WebKit/Source/core/frame/Window.idl b/third_party/WebKit/Source/core/frame/Window.idl index 8b45bb3a..c4a747f 100644 --- a/third_party/WebKit/Source/core/frame/Window.idl +++ b/third_party/WebKit/Source/core/frame/Window.idl
@@ -38,7 +38,7 @@ [Replaceable, CrossOrigin] readonly attribute Window self; [Unforgeable, CachedAccessor] readonly attribute Document document; attribute DOMString name; - [PutForwards=href, Unforgeable, CrossOrigin=(Getter,Setter)] readonly attribute Location location; + [PutForwards=href, Unforgeable, CrossOrigin=(Getter,Setter), Custom=Getter] readonly attribute Location location; readonly attribute History history; [Replaceable, MeasureAs=BarPropLocationbar] readonly attribute BarProp locationbar; [Replaceable, MeasureAs=BarPropMenubar] readonly attribute BarProp menubar;
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp index c12b486..afd5e73 100644 --- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -3449,7 +3449,7 @@ } bool HTMLMediaElement::isFullscreen() const { - return Fullscreen::isFullscreenElement(*this); + return Fullscreen::isCurrentFullScreenElement(*this); } void HTMLMediaElement::didEnterFullscreen() {
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlsOrientationLockDelegateTest.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControlsOrientationLockDelegateTest.cpp index ad490be..cb58274 100644 --- a/third_party/WebKit/Source/core/html/shadow/MediaControlsOrientationLockDelegateTest.cpp +++ b/third_party/WebKit/Source/core/html/shadow/MediaControlsOrientationLockDelegateTest.cpp
@@ -169,13 +169,13 @@ Fullscreen::requestFullscreen(video()); Fullscreen::from(document()).didEnterFullscreen(); - document().serviceScriptedAnimations(WTF::monotonicallyIncreasingTime()); + testing::runPendingTasks(); } void simulateExitFullscreen() { Fullscreen::exitFullscreen(document()); Fullscreen::from(document()).didExitFullscreen(); - document().serviceScriptedAnimations(WTF::monotonicallyIncreasingTime()); + testing::runPendingTasks(); } void simulateOrientationLock() {
diff --git a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp index 34621286..2067dba 100644 --- a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp
@@ -2042,6 +2042,13 @@ if (needsToFulfillMinimumSize) ++repetitions; + // Clamp the number of repetitions so we don't end up with too many tracks. + if (repetitions > kGridMaxTracks) { + DCHECK_GT(autoRepeatTrackListLength, 0u); + repetitions = + (kGridMaxTracks - trackSizes.size()) / autoRepeatTrackListLength; + } + return repetitions * autoRepeatTrackListLength; }
diff --git a/third_party/WebKit/Source/core/layout/LayoutInline.cpp b/third_party/WebKit/Source/core/layout/LayoutInline.cpp index 4b1b406c..1e9173c2 100644 --- a/third_party/WebKit/Source/core/layout/LayoutInline.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutInline.cpp
@@ -402,9 +402,9 @@ // not its parent. Since the splitting logic expects |this| to be the parent, // set |beforeChild| to be the LayoutFullScreen. if (Fullscreen* fullscreen = Fullscreen::fromIfExists(document())) { - const Element* fullscreenElement = fullscreen->fullscreenElement(); - if (fullscreenElement && beforeChild && - beforeChild->node() == fullscreenElement) + const Element* fullScreenElement = fullscreen->currentFullScreenElement(); + if (fullScreenElement && beforeChild && + beforeChild->node() == fullScreenElement) beforeChild = fullscreen->fullScreenLayoutObject(); }
diff --git a/third_party/WebKit/Source/core/layout/compositing/PaintLayerCompositor.cpp b/third_party/WebKit/Source/core/layout/compositing/PaintLayerCompositor.cpp index d649037..10265b8 100644 --- a/third_party/WebKit/Source/core/layout/compositing/PaintLayerCompositor.cpp +++ b/third_party/WebKit/Source/core/layout/compositing/PaintLayerCompositor.cpp
@@ -170,6 +170,11 @@ return nullptr; fullscreenElement = Fullscreen::fullscreenElementFrom(*contentDocument); } + // Get the current fullscreen element from the document. + // TODO(foolip): When |currentFullScreenElementFrom| is removed, this will + // become a no-op and can be removed. https://crbug.com/402421 + fullscreenElement = + Fullscreen::currentFullScreenElementFrom(*contentDocument); if (!isHTMLVideoElement(fullscreenElement)) return nullptr; LayoutObject* layoutObject = fullscreenElement->layoutObject();
diff --git a/third_party/WebKit/Source/core/page/ChromeClient.h b/third_party/WebKit/Source/core/page/ChromeClient.h index 7d91e79..f4492e9b 100644 --- a/third_party/WebKit/Source/core/page/ChromeClient.h +++ b/third_party/WebKit/Source/core/page/ChromeClient.h
@@ -251,8 +251,7 @@ virtual void enterFullscreen(LocalFrame&) {} virtual void exitFullscreen(LocalFrame&) {} - virtual void fullscreenElementChanged(Element* fromElement, - Element* toElement) {} + virtual void fullscreenElementChanged(Element*, Element*) {} virtual void clearCompositedSelection(LocalFrame*) {} virtual void updateCompositedSelection(LocalFrame*,
diff --git a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp index 829d1de2..43cd97d4 100644 --- a/third_party/WebKit/Source/modules/vr/VRDisplay.cpp +++ b/third_party/WebKit/Source/modules/vr/VRDisplay.cpp
@@ -669,7 +669,7 @@ // depend on the Fullscreen API to fake VR presentation, so this will // become unnessecary. Until that point, though, this seems preferable to // adding a bunch of notification plumbing to Fullscreen. - if (!Fullscreen::isFullscreenElement(*m_layer.source())) { + if (!Fullscreen::isCurrentFullScreenElement(*m_layer.source())) { // TODO(mthiesse): Due to asynchronous resizing, we might get kicked out of // fullscreen when changing display parameters upon entering WebVR. So one // time only, we reenter fullscreen after having left it; otherwise we exit
diff --git a/third_party/WebKit/Source/platform/exported/WebString.cpp b/third_party/WebKit/Source/platform/exported/WebString.cpp index 4dc2a65..29205801 100644 --- a/third_party/WebKit/Source/platform/exported/WebString.cpp +++ b/third_party/WebKit/Source/platform/exported/WebString.cpp
@@ -90,10 +90,6 @@ return String::fromUTF8(data, length); } -WebString WebString::fromUTF8(const char* data) { - return String::fromUTF8(data); -} - WebString WebString::fromUTF16(const base::string16& s) { WebString string; string.assign(s.data(), s.length());
diff --git a/third_party/WebKit/Source/web/ChromeClientImpl.h b/third_party/WebKit/Source/web/ChromeClientImpl.h index c74b0ab..1daf994 100644 --- a/third_party/WebKit/Source/web/ChromeClientImpl.h +++ b/third_party/WebKit/Source/web/ChromeClientImpl.h
@@ -163,8 +163,7 @@ void enterFullscreen(LocalFrame&) override; void exitFullscreen(LocalFrame&) override; - void fullscreenElementChanged(Element* fromElement, - Element* toElement) override; + void fullscreenElementChanged(Element*, Element*) override; void clearCompositedSelection(LocalFrame*) override; void updateCompositedSelection(LocalFrame*,
diff --git a/third_party/WebKit/Source/web/FullscreenController.cpp b/third_party/WebKit/Source/web/FullscreenController.cpp index 7470776..4380a01 100644 --- a/third_party/WebKit/Source/web/FullscreenController.cpp +++ b/third_party/WebKit/Source/web/FullscreenController.cpp
@@ -88,9 +88,6 @@ fullscreen->didEnterFullscreen(); } } - - // TODO(foolip): If the top level browsing context (main frame) ends up with - // no fullscreen element, exit fullscreen again to recover. } void FullscreenController::didExitFullscreen() { @@ -103,32 +100,33 @@ updatePageScaleConstraints(true); - // We need to wait until style and layout are updated in order to properly - // restore scroll offsets since content may not be overflowing in the same way - // until they are. - m_state = State::NeedsScrollAndScaleRestore; + // Set |m_state| so that any |exitFullscreen()| calls from within + // |Fullscreen::didExitFullscreen()| do not call + // |WebFrameClient::exitFullscreen()| again. + // TODO(foolip): Remove this when state changes and events are synchronized + // with animation frames. https://crbug.com/402376 + m_state = State::ExitingFullscreen; - // Notify the topmost local frames that we have exited fullscreen. - // |Fullscreen::didExitFullscreen()| will take care of descendant frames. - for (Frame* frame = m_webViewImpl->page()->mainFrame(); frame;) { - Frame* nextFrame = frame->tree().traverseNext(); - - if (frame->isRemoteFrame()) { - frame = nextFrame; + // Notify all local frames that we have exited fullscreen. + // TODO(foolip): This should only need to notify the topmost local roots. That + // doesn't currently work because |Fullscreen::m_currentFullScreenElement| + // isn't set for the topmost document when an iframe goes fullscreen, but can + // be done once |m_currentFullScreenElement| is gone and all state is in the + // fullscreen element stack. https://crbug.com/402421 + for (Frame* frame = m_webViewImpl->page()->mainFrame(); frame; + frame = frame->tree().traverseNext()) { + if (!frame->isLocalFrame()) continue; - } - - DCHECK(frame->isLocalRoot()); if (Document* document = toLocalFrame(frame)->document()) { if (Fullscreen* fullscreen = Fullscreen::fromIfExists(*document)) fullscreen->didExitFullscreen(); } - - // Skip over all descendant frames. - while (nextFrame && nextFrame->tree().isDescendantOf(frame)) - nextFrame = nextFrame->tree().traverseNext(); - frame = nextFrame; } + + // We need to wait until style and layout are updated in order to properly + // restore scroll offsets since content may not be overflowing in the same way + // until they are. + m_state = State::NeedsScrollAndScaleRestore; } void FullscreenController::enterFullscreen(LocalFrame& frame) { @@ -185,7 +183,7 @@ DCHECK_NE(fromElement, toElement); if (toElement) { - DCHECK(Fullscreen::isFullscreenElement(*toElement)); + DCHECK(Fullscreen::isCurrentFullScreenElement(*toElement)); if (isHTMLVideoElement(*toElement)) { HTMLVideoElement& videoElement = toHTMLVideoElement(*toElement); @@ -201,7 +199,7 @@ } if (fromElement) { - DCHECK(!Fullscreen::isFullscreenElement(*fromElement)); + DCHECK(!Fullscreen::isCurrentFullScreenElement(*fromElement)); if (isHTMLVideoElement(*fromElement)) { // If the video used overlay fullscreen mode, restore the transparency.
diff --git a/third_party/WebKit/Source/web/FullscreenController.h b/third_party/WebKit/Source/web/FullscreenController.h index de3e34d..4ffbd6d 100644 --- a/third_party/WebKit/Source/web/FullscreenController.h +++ b/third_party/WebKit/Source/web/FullscreenController.h
@@ -58,7 +58,7 @@ // Called by Fullscreen (via ChromeClient) to notify that the fullscreen // element has changed. - void fullscreenElementChanged(Element* fromElement, Element* toElement); + void fullscreenElementChanged(Element*, Element*); bool isFullscreen() { return m_state == State::Fullscreen; }
diff --git a/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp b/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp index f30d629..4301e30 100644 --- a/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp +++ b/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp
@@ -311,7 +311,7 @@ } bool WebPluginContainerImpl::isFullscreenElement() const { - return Fullscreen::isFullscreenElement(*m_element); + return Fullscreen::isCurrentFullScreenElement(*m_element); } void WebPluginContainerImpl::cancelFullscreen() {
diff --git a/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp b/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp index ec573b5..29479f8 100644 --- a/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp +++ b/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
@@ -506,21 +506,24 @@ HTMLFrameOwnerElement* ownerElement = toHTMLFrameOwnerElement(frame()->owner()); - // Call |requestFullscreen()| on |ownerElement| to make it the pending - // fullscreen element in anticipation of the coming |didEnterFullscreen()| - // call. + // Call requestFullscreen() on |ownerElement| to make it the provisional + // fullscreen element in FullscreenController, and to prepare + // fullscreenchange events that will need to fire on it and its (local) + // ancestors. The events will be triggered if/when fullscreen is entered. // - // PrefixedForCrossProcessDescendant is necessary because: - // - The fullscreen element ready check and other checks should be bypassed. - // - |ownerElement| will need :-webkit-full-screen-ancestor style in addition - // to :-webkit-full-screen. + // Passing |forCrossProcessAncestor| to requestFullscreen is necessary + // because: + // - |ownerElement| will need :-webkit-full-screen-ancestor style in + // addition to :-webkit-full-screen. + // - there's no need to resend the ToggleFullscreen IPC to the browser + // process. // // TODO(alexmos): currently, this assumes prefixed requests, but in the // future, this should plumb in information about which request type // (prefixed or unprefixed) to use for firing fullscreen events. - Fullscreen::requestFullscreen( - *ownerElement, - Fullscreen::RequestType::PrefixedForCrossProcessDescendant); + Fullscreen::requestFullscreen(*ownerElement, + Fullscreen::RequestType::Prefixed, + true /* forCrossProcessAncestor */); } void WebRemoteFrameImpl::setHasReceivedUserGesture() {
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h index e88da4c..f648bad5 100644 --- a/third_party/WebKit/Source/web/WebViewImpl.h +++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -443,7 +443,7 @@ void enterFullscreen(LocalFrame&); void exitFullscreen(LocalFrame&); - void fullscreenElementChanged(Element* fromElement, Element* toElement); + void fullscreenElementChanged(Element*, Element*); // Exposed for the purpose of overriding device metrics. void sendResizeEventAndRepaint();
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp index cceac88..70d4e1f8d 100644 --- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp +++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -7666,11 +7666,14 @@ UserGestureIndicator gesture(DocumentUserGestureToken::create(document)); Element* divFullscreen = document->getElementById("div1"); Fullscreen::requestFullscreen(*divFullscreen); + EXPECT_EQ(nullptr, Fullscreen::currentFullScreenElementFrom(*document)); + EXPECT_EQ(divFullscreen, Fullscreen::fullscreenElementFrom(*document)); webViewImpl->didEnterFullscreen(); - EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*document)); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); + EXPECT_EQ(divFullscreen, Fullscreen::currentFullScreenElementFrom(*document)); EXPECT_EQ(divFullscreen, Fullscreen::fullscreenElementFrom(*document)); webViewImpl->updateAllLifecyclePhases(); + EXPECT_EQ(divFullscreen, Fullscreen::currentFullScreenElementFrom(*document)); + EXPECT_EQ(divFullscreen, Fullscreen::fullscreenElementFrom(*document)); // Verify that the element is sized to the viewport. LayoutFullScreen* fullscreenLayoutObject = @@ -7703,11 +7706,14 @@ UserGestureIndicator gesture(DocumentUserGestureToken::create(document)); Element* divFullscreen = document->getElementById("div1"); Fullscreen::requestFullscreen(*divFullscreen); + EXPECT_EQ(nullptr, Fullscreen::currentFullScreenElementFrom(*document)); + EXPECT_EQ(divFullscreen, Fullscreen::fullscreenElementFrom(*document)); webViewImpl->didEnterFullscreen(); - EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*document)); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); + EXPECT_EQ(divFullscreen, Fullscreen::currentFullScreenElementFrom(*document)); EXPECT_EQ(divFullscreen, Fullscreen::fullscreenElementFrom(*document)); webViewImpl->updateAllLifecyclePhases(); + EXPECT_EQ(divFullscreen, Fullscreen::currentFullScreenElementFrom(*document)); + EXPECT_EQ(divFullscreen, Fullscreen::fullscreenElementFrom(*document)); // Verify that the viewports are nonscrollable. FrameView* frameView = webViewHelper.webView()->mainFrameImpl()->frameView(); @@ -7724,11 +7730,14 @@ ASSERT_FALSE(visualViewportScrollLayer->userScrollableVertical()); // Verify that the viewports are scrollable upon exiting fullscreen. - webViewImpl->didExitFullscreen(); + EXPECT_EQ(divFullscreen, Fullscreen::currentFullScreenElementFrom(*document)); EXPECT_EQ(divFullscreen, Fullscreen::fullscreenElementFrom(*document)); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); + webViewImpl->didExitFullscreen(); + EXPECT_EQ(nullptr, Fullscreen::currentFullScreenElementFrom(*document)); EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*document)); webViewImpl->updateAllLifecyclePhases(); + EXPECT_EQ(nullptr, Fullscreen::currentFullScreenElementFrom(*document)); + EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*document)); ASSERT_TRUE(layoutViewportScrollLayer->userScrollableHorizontal()); ASSERT_TRUE(layoutViewportScrollLayer->userScrollableVertical()); ASSERT_TRUE(visualViewportScrollLayer->userScrollableHorizontal()); @@ -7750,12 +7759,20 @@ Document* document = webViewImpl->mainFrameImpl()->frame()->document(); UserGestureIndicator gesture(DocumentUserGestureToken::create(document)); Fullscreen::requestFullscreen(*document->documentElement()); - webViewImpl->didEnterFullscreen(); - EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*document)); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); + EXPECT_EQ(nullptr, Fullscreen::currentFullScreenElementFrom(*document)); EXPECT_EQ(document->documentElement(), Fullscreen::fullscreenElementFrom(*document)); + webViewImpl->didEnterFullscreen(); + EXPECT_EQ(document->documentElement(), + Fullscreen::currentFullScreenElementFrom(*document)); + EXPECT_EQ(document->documentElement(), + Fullscreen::fullscreenElementFrom(*document)); + webViewImpl->updateAllLifecyclePhases(); + EXPECT_EQ(document->documentElement(), + Fullscreen::currentFullScreenElementFrom(*document)); + EXPECT_EQ(document->documentElement(), + Fullscreen::fullscreenElementFrom(*document)); // Verify that the main frame is still scrollable. WebLayer* webScrollLayer = @@ -7794,7 +7811,6 @@ Element* divFullscreen = document->getElementById("div1"); Fullscreen::requestFullscreen(*divFullscreen); webViewImpl->didEnterFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); // Verify that the element is sized to the viewport. @@ -7836,11 +7852,6 @@ Fullscreen::requestFullscreen(*topBody); } webViewImpl->didEnterFullscreen(); - EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*topDoc)); - EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*iframeDoc)); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); - EXPECT_EQ(topBody, Fullscreen::fullscreenElementFrom(*topDoc)); - EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*iframeDoc)); webViewImpl->updateAllLifecyclePhases(); { @@ -7848,25 +7859,23 @@ Fullscreen::requestFullscreen(*iframeBody); } webViewImpl->didEnterFullscreen(); - EXPECT_EQ(topBody, Fullscreen::fullscreenElementFrom(*topDoc)); - EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*iframeDoc)); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); - EXPECT_EQ(iframe, Fullscreen::fullscreenElementFrom(*topDoc)); - EXPECT_EQ(iframeBody, Fullscreen::fullscreenElementFrom(*iframeDoc)); webViewImpl->updateAllLifecyclePhases(); // We are now in nested fullscreen, with both documents having a non-empty // fullscreen element stack. + EXPECT_EQ(topBody, Fullscreen::currentFullScreenElementFrom(*topDoc)); + EXPECT_EQ(iframe, Fullscreen::fullscreenElementFrom(*topDoc)); + EXPECT_EQ(iframeBody, Fullscreen::currentFullScreenElementFrom(*iframeDoc)); + EXPECT_EQ(iframeBody, Fullscreen::fullscreenElementFrom(*iframeDoc)); webViewImpl->didExitFullscreen(); - EXPECT_EQ(iframe, Fullscreen::fullscreenElementFrom(*topDoc)); - EXPECT_EQ(iframeBody, Fullscreen::fullscreenElementFrom(*iframeDoc)); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); - EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*topDoc)); - EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*iframeDoc)); webViewImpl->updateAllLifecyclePhases(); - // We have now fully exited fullscreen. + // We should now have fully exited fullscreen. + EXPECT_EQ(nullptr, Fullscreen::currentFullScreenElementFrom(*topDoc)); + EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*topDoc)); + EXPECT_EQ(nullptr, Fullscreen::currentFullScreenElementFrom(*iframeDoc)); + EXPECT_EQ(nullptr, Fullscreen::fullscreenElementFrom(*iframeDoc)); } TEST_P(ParameterizedWebFrameTest, FullscreenWithTinyViewport) { @@ -7895,7 +7904,6 @@ UserGestureIndicator gesture(DocumentUserGestureToken::create(document)); Fullscreen::requestFullscreen(*document->documentElement()); webViewImpl->didEnterFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); EXPECT_EQ(384, layoutViewItem.logicalWidth().floor()); EXPECT_EQ(640, layoutViewItem.logicalHeight().floor()); @@ -7904,7 +7912,6 @@ EXPECT_FLOAT_EQ(1.0, webViewImpl->maximumPageScaleFactor()); webViewImpl->didExitFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); EXPECT_EQ(320, layoutViewItem.logicalWidth().floor()); EXPECT_EQ(533, layoutViewItem.logicalHeight().floor()); @@ -7933,7 +7940,6 @@ UserGestureIndicator gesture(DocumentUserGestureToken::create(document)); Fullscreen::requestFullscreen(*document->documentElement()); webViewImpl->didEnterFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); EXPECT_EQ(384, layoutViewItem.logicalWidth().floor()); EXPECT_EQ(640, layoutViewItem.logicalHeight().floor()); @@ -7954,7 +7960,6 @@ EXPECT_FLOAT_EQ(1.0, webViewImpl->maximumPageScaleFactor()); webViewImpl->didExitFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); EXPECT_EQ(320, layoutViewItem.logicalWidth().floor()); EXPECT_EQ(192, layoutViewItem.logicalHeight().floor()); @@ -7998,7 +8003,6 @@ } webViewImpl->didEnterFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); client.m_screenInfo.rect.width = screenSizeMinusStatusBars.width; client.m_screenInfo.rect.height = screenSizeMinusStatusBars.height; @@ -8013,7 +8017,6 @@ EXPECT_FLOAT_EQ(1.0, webViewImpl->maximumPageScaleFactor()); webViewImpl->didExitFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); client.m_screenInfo.rect.width = screenSizeMinusStatusBars.width; client.m_screenInfo.rect.height = screenSizeMinusStatusBars.height; @@ -8059,7 +8062,6 @@ DocumentUserGestureToken::create(document, UserGestureToken::NewGesture)); Fullscreen::requestFullscreen(*document->documentElement()); webViewImpl->didEnterFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); // Entering fullscreen causes layout size and page scale limits to be @@ -8077,7 +8079,6 @@ WebFrame* frame = webViewHelper.webView()->mainFrame(); FrameTestHelpers::loadHTMLString(frame, source, testURL); webViewImpl->didExitFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); // Make sure the new page's layout size and scale factor limits aren't @@ -8125,23 +8126,19 @@ HTMLVideoElement* video = toHTMLVideoElement(document->getElementById("video")); EXPECT_TRUE(video->usesOverlayFullscreenVideo()); + EXPECT_FALSE(video->isFullscreen()); + EXPECT_FALSE(layerTreeView.hasTransparentBackground); video->webkitEnterFullscreen(); webViewImpl->didEnterFullscreen(); - EXPECT_FALSE(video->isFullscreen()); - EXPECT_FALSE(layerTreeView.hasTransparentBackground); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); + webViewImpl->updateAllLifecyclePhases(); EXPECT_TRUE(video->isFullscreen()); EXPECT_TRUE(layerTreeView.hasTransparentBackground); - webViewImpl->updateAllLifecyclePhases(); webViewImpl->didExitFullscreen(); - EXPECT_TRUE(video->isFullscreen()); - EXPECT_TRUE(layerTreeView.hasTransparentBackground); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); + webViewImpl->updateAllLifecyclePhases(); EXPECT_FALSE(video->isFullscreen()); EXPECT_FALSE(layerTreeView.hasTransparentBackground); - webViewImpl->updateAllLifecyclePhases(); } TEST_P(ParameterizedWebFrameTest, LayoutBlockPercentHeightDescendants) {
diff --git a/third_party/WebKit/Source/web/tests/WebViewTest.cpp b/third_party/WebKit/Source/web/tests/WebViewTest.cpp index 4a6c50a..a727e0d3 100644 --- a/third_party/WebKit/Source/web/tests/WebViewTest.cpp +++ b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
@@ -1753,7 +1753,6 @@ UserGestureIndicator gesture(DocumentUserGestureToken::create(document)); Fullscreen::requestFullscreen(*element); webViewImpl->didEnterFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); // Sanity-check. There should be no scrolling possible. @@ -1767,12 +1766,10 @@ // parameters are reset. The page sets display: none on overflowing elements // while in fullscreen so if we try to restore before the style and layout // is applied the offsets will be clamped. - EXPECT_FALSE(webViewImpl->mainFrameImpl()->frameView()->needsLayout()); webViewImpl->didExitFullscreen(); EXPECT_TRUE(webViewImpl->mainFrameImpl()->frameView()->needsLayout()); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); - EXPECT_EQ(0, webViewImpl->mainFrame()->getScrollOffset().height); webViewImpl->updateAllLifecyclePhases(); + EXPECT_EQ(2000, webViewImpl->mainFrame()->getScrollOffset().height); } @@ -1797,7 +1794,6 @@ UserGestureIndicator gesture(DocumentUserGestureToken::create(document)); Fullscreen::requestFullscreen(*element); webViewImpl->didEnterFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); // Sanity-check. There should be no scrolling possible. @@ -1813,7 +1809,6 @@ webViewImpl->didExitFullscreen(); Fullscreen::requestFullscreen(*element); webViewImpl->didEnterFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); // Sanity-check. There should be no scrolling possible. @@ -1825,7 +1820,6 @@ // When we exit now, we should restore the original scroll value. webViewImpl->didExitFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); EXPECT_EQ(2000, webViewImpl->mainFrame()->getScrollOffset().height); @@ -1857,8 +1851,6 @@ UserGestureIndicator gesture(DocumentUserGestureToken::create(document)); Fullscreen::requestFullscreen(*element); webViewImpl->didEnterFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); - webViewImpl->updateAllLifecyclePhases(); // Page scale factor must be 1.0 during fullscreen for elements to be sized // properly. @@ -1870,7 +1862,6 @@ // Confirm that exiting fullscreen restores the parameters. webViewImpl->didExitFullscreen(); - webViewImpl->beginFrame(WTF::monotonicallyIncreasingTime()); webViewImpl->updateAllLifecyclePhases(); EXPECT_EQ(2.0f, webViewImpl->pageScaleFactor());
diff --git a/third_party/WebKit/public/platform/WebString.h b/third_party/WebKit/public/platform/WebString.h index 85a6832..c8cbf0f0 100644 --- a/third_party/WebKit/public/platform/WebString.h +++ b/third_party/WebKit/public/platform/WebString.h
@@ -113,6 +113,9 @@ BLINK_COMMON_EXPORT bool equals(const WebString&) const; BLINK_COMMON_EXPORT bool equals(const char* characters, size_t len) const; + bool equals(const char* characters) const { + return equals(characters, characters ? strlen(characters) : 0); + } BLINK_COMMON_EXPORT size_t length() const; @@ -124,8 +127,6 @@ BLINK_COMMON_EXPORT static WebString fromUTF8(const char* data, size_t length); - BLINK_COMMON_EXPORT static WebString fromUTF8(const char* data); - static WebString fromUTF8(const std::string& s) { return fromUTF8(s.data(), s.length()); } @@ -229,7 +230,7 @@ }; inline bool operator==(const WebString& a, const char* b) { - return a.equals(b, b ? strlen(b) : 0); + return a.equals(b); } inline bool operator!=(const WebString& a, const char* b) {
diff --git a/third_party/openh264/BUILD.gn b/third_party/openh264/BUILD.gn index 4301abd..7b9d458 100644 --- a/third_party/openh264/BUILD.gn +++ b/third_party/openh264/BUILD.gn
@@ -2,8 +2,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/sanitizers/sanitizers.gni") import("//third_party/openh264/openh264_args.gni") import("//third_party/openh264/openh264_sources.gni") +import("//third_party/yasm/yasm_assemble.gni") # Config shared by all openh264 targets. config("config") { @@ -28,6 +30,77 @@ } } +# YASM assembly is only checked to be working on Windows and Linux. +# Mac is known to fail certain tests when building, but actual assembly +# is believed to work. +# MSAN builds are flaky with assembler. crbug.com/685168 + +use_assembler = (is_win || is_linux) && + (target_cpu == "x86" || target_cpu == "x64") && !is_msan + +# This IF statement will make the targets visible only on specific builds, +# which will lead to failures on other platforms if accidentally invoked. +if (use_assembler) { + yasm_assemble("openh264_common_yasm") { + include_dirs = openh264_common_include_dirs + sources = openh264_common_sources_asm_x86 + if (target_cpu == "x86") { + defines = [ "X86_32" ] + } else { # x64 + if (is_mac) { + defines = [ + "PREFIX", + "UNIX64", + ] + } else if (is_win) { + defines = [ "WIN64" ] + } else if (is_linux) { + defines = [ "UNIX64" ] + } + } + } + + yasm_assemble("openh264_processing_yasm") { + include_dirs = openh264_processing_include_dirs + include_dirs += [ "./src/codec/common/x86" ] + sources = openh264_processing_sources_asm_x86 + if (target_cpu == "x86") { + defines = [ "X86_32" ] + } else { # x64 + if (is_mac) { + defines = [ + "PREFIX", + "UNIX64", + ] + } else if (is_win) { + defines = [ "WIN64" ] + } else if (is_linux) { + defines = [ "UNIX64" ] + } + } + } + + yasm_assemble("openh264_encoder_yasm") { + include_dirs = openh264_encoder_include_dirs + include_dirs += [ "./src/codec/common/x86" ] + sources = openh264_encoder_sources_asm_x86 + if (target_cpu == "x86") { + defines = [ "X86_32" ] + } else { # x64 + if (is_mac) { + defines = [ + "PREFIX", + "UNIX64", + ] + } else if (is_win) { + defines = [ "WIN64" ] + } else if (is_linux) { + defines = [ "UNIX64" ] + } + } + } +} # if (is_win || is_linux) + source_set("common") { sources = openh264_common_sources include_dirs = openh264_common_include_dirs @@ -36,6 +109,10 @@ configs += [ "//build/config/compiler:no_chromium_code" ] configs += [ ":config" ] deps = [] + if (use_assembler) { + defines = [ "X86_ASM" ] + deps += [ ":openh264_common_yasm" ] + } if (is_android) { deps += [ # Defines "android_get/setCpu..." functions. The original OpenH264 build @@ -57,6 +134,10 @@ deps = [ ":common", ] + if (use_assembler) { + defines = [ "X86_ASM" ] + deps += [ ":openh264_processing_yasm" ] + } } source_set("encoder") { @@ -76,4 +157,8 @@ ":common", ":processing", ] + if (use_assembler) { + defines = [ "X86_ASM" ] + deps += [ ":openh264_encoder_yasm" ] + } }
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 47e4ad8..eef0a37 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -5053,6 +5053,19 @@ </summary> </histogram> +<histogram name="Bluetooth.Web.Descriptor.ReadValue.Outcome" + enum="WebBluetoothGATTOperationOutcome"> + <owner>jyasskin@chromium.org</owner> + <owner>ortuno@chromium.org</owner> + <owner>scheib@chromium.org</owner> + <summary> + Records the outcome of a call to descriptor.readValue(). Used to know what + types of errors users are seeing. The results will be used to determine how + common these errors are and if we need to provide better error messages to + the users. + </summary> +</histogram> + <histogram name="Bluetooth.Web.FunctionCall.Count" enum="WebBluetoothFunction"> <owner>jyasskin@chromium.org</owner> <owner>ortuno@chromium.org</owner> @@ -54065,10 +54078,7 @@ </histogram> <histogram name="ResourcePrefetchPredictor.HistoryVisitCountForUrl"> - <obsolete> - Deprecated September 2016. No longer recorded. - </obsolete> - <owner>zhenw@chromium.org</owner> + <owner>alexilin@chromium.org</owner> <summary> The visit count of a URL in the history database, measured when the onload fires for the URL. Helpful in figuring out what visit count should be used @@ -54280,6 +54290,11 @@ </histogram> <histogram name="ResourcePrefetchPredictor.HostTableHostCount"> + <obsolete> + Deprecated January 2017. This is effectively the same as + ResourcePrefetchPredictor.HostTableRowCount2 after refactoring of the + predictor database. + </obsolete> <owner>zhenw@chromium.org</owner> <summary> The count of number of unique hosts in the HostTable, i.e. the number of @@ -54290,6 +54305,10 @@ </histogram> <histogram name="ResourcePrefetchPredictor.HostTableRowCount"> + <obsolete> + Deprecated January 2017. Replaced by + ResourcePrefetchPredictor.HostTableRowCount2. + </obsolete> <owner>zhenw@chromium.org</owner> <summary> The count of number of rows in the HostTable. This is effecively the number @@ -54298,6 +54317,15 @@ </summary> </histogram> +<histogram name="ResourcePrefetchPredictor.HostTableRowCount2" units="hosts"> + <owner>alexilin@chromium.org</owner> + <summary> + The number of rows in the HostTable. This is effectively the number of hosts + in the database. This is measured at startup and used to get an estimate of + the data size. + </summary> +</histogram> + <histogram name="ResourcePrefetchPredictor.LearningPrecision" units="%"> <owner>lizeb@chromium.org</owner> <summary> @@ -54318,7 +54346,7 @@ <histogram name="ResourcePrefetchPredictor.MainFrameRequestStats" enum="ResourcePrefetchPredictorMainFrameRequestStats"> - <owner>zhenw@chromium.org</owner> + <owner>alexilin@chromium.org</owner> <summary> Records stats about main frame navigations. Records the total number of requests/responses/redirects for main frame urls along with the numbers for @@ -54464,7 +54492,7 @@ <histogram name="ResourcePrefetchPredictor.ReportingEvent" enum="ResourcePrefetchPredictorReportingEvent"> - <owner>zhenw@chromium.org</owner> + <owner>alexilin@chromium.org</owner> <summary> Records stats about various interesting events such as - when partial or all of history is cleared. It will include events which do not necessarily @@ -54475,7 +54503,7 @@ <histogram name="ResourcePrefetchPredictor.RequestStats" enum="ResourcePrefetchPredictorRequestStats"> - <owner>zhenw@chromium.org</owner> + <owner>alexilin@chromium.org</owner> <summary> Records stats about requests, redirects, and responses observed by the ResourcePrefetchPredictorObserver. These stats are useful as a baseline for @@ -54702,7 +54730,9 @@ <histogram name="ResourcePrefetchPredictor.UrlTableMainFrameUrlCount"> <obsolete> - Deprecated September 2016. No longer recorded. + Deprecated September 2016. This is effectively the same as + ResourcePrefetchPredictor.UrlTableRowCount2 after refactoring of the + predictor database. </obsolete> <owner>zhenw@chromium.org</owner> <summary> @@ -54715,6 +54745,9 @@ <histogram name="ResourcePrefetchPredictor.UrlTableMainFrameUrlsDeletedNotInHistory"> + <obsolete> + Deprecated October 2012. No longer recorded. + </obsolete> <owner>zhenw@chromium.org</owner> <summary> The count of number of unique main frame urls that are deleted from the URL @@ -54726,6 +54759,9 @@ <histogram name="ResourcePrefetchPredictor.UrlTableMainFrameUrlsDeletedNotInHistoryPercent" units="%"> + <obsolete> + Deprecated October 2012. No longer recorded. + </obsolete> <owner>zhenw@chromium.org</owner> <summary> Same as ResourcePrefetchPredictor.UrlTableMainFrameUrlsDeletedNotInHistory @@ -54734,6 +54770,10 @@ </histogram> <histogram name="ResourcePrefetchPredictor.UrlTableRowCount"> + <obsolete> + Deprecated January 2017. Replaced by + ResourcePrefetchPredictor.UrlTableRowCount2. + </obsolete> <owner>zhenw@chromium.org</owner> <summary> The count of number of rows in the UrlTable. This is effecively the number @@ -54742,6 +54782,15 @@ </summary> </histogram> +<histogram name="ResourcePrefetchPredictor.UrlTableRowCount2" units="urls"> + <owner>alexilin@chromium.org</owner> + <summary> + The number of rows in the UrlTable. This is effectively the number of main + frame URLs in the database. This is measured at startup and used to get an + estimate of the data size. + </summary> +</histogram> + <histogram name="ResourceReporter.BrowserProcess.CpuUsage" enum="ResourceReporterCpuUsage"> <owner>afakhry@chromium.org</owner> @@ -117080,6 +117129,9 @@ </histogram_suffixes> <histogram_suffixes name="ResourcePrefetchPredictorPLTPrefetch" separator="."> + <obsolete> + Deprecated September 2016. No longer recorded. + </obsolete> <suffix name="NotPrefetched" label="Page load time for non-prefetched pages."/> <suffix name="Prefetched" label="Page load time for prefetched pages."/> @@ -117088,6 +117140,9 @@ <histogram_suffixes name="ResourcePrefetchPredictorPLTPrefetchType" separator="."> + <obsolete> + Deprecated September 2016. No longer recorded. + </obsolete> <suffix name="Host" label="Page load time for prefetched pages based on main frame host."/> <suffix name="Url"
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc index 3b848bc..04c5a94 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -1094,7 +1094,13 @@ // X server opacity is in terms of 32 bit unsigned int space, and counts from // the opposite direction. // XChangeProperty() expects "cardinality" to be long. - unsigned long cardinality = static_cast<int>(opacity * 255) * 0x1010101; + + // Scale opacity to [0 .. 255] range. + unsigned long opacity_8bit = + static_cast<unsigned long>(opacity * 255.0f) & 0xFF; + // Use opacity value for all channels. + const unsigned long channel_multiplier = 0x1010101; + unsigned long cardinality = opacity_8bit * channel_multiplier; if (cardinality == 0xffffffff) { XDeleteProperty(xdisplay_, xwindow_,
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc index 4d0e7cf9..6063a224 100644 --- a/ui/views/widget/widget.cc +++ b/ui/views/widget/widget.cc
@@ -700,6 +700,8 @@ } void Widget::SetOpacity(float opacity) { + DCHECK(opacity >= 0.0f); + DCHECK(opacity <= 1.0f); native_widget_->SetOpacity(opacity); }