diff --git a/DEPS b/DEPS index aef4e43..f8c364a 100644 --- a/DEPS +++ b/DEPS
@@ -138,11 +138,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '62fd2c32bbe637b15e9dbc4b3c15ebd3291597ea', + 'skia_revision': 'ac79ca16c651f020a4c257d75919b00eca61b494', # 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': '9672bf6d0b325ae9a9157ca217db965bca7e76f9', + 'v8_revision': '10dea84f8dfc007114fa953fb00cb721afd98e69', # 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. @@ -154,7 +154,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': '75841d73c2de727355d48d8d9af9f6cd7e3f7738', + 'swiftshader_revision': '5b424e69101a103a630327b8de7a6636dfddea2c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. @@ -201,7 +201,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': 'f8aef9991ba929e8e7c202fbcebccce376ffc726', + 'catapult_revision': 'dd7a2ceeb49ed46f23f9048b0b7fbecfb84461db', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -832,7 +832,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0f476788122d4b40b0293226e193e534ec66cad6', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '71c6bc07e67f3a09b9e03d672a154c3b638103a2', 'src/third_party/devtools-node-modules': Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'), @@ -901,7 +901,7 @@ }, 'src/third_party/glslang/src': - Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'a549bb81752365526f6d7334f00961ea08689211', + Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '2f4a8dfd3a596d75e3c26cb7ae9b68886d3a19cf', 'src/third_party/google_toolbox_for_mac/src': { 'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'), @@ -1164,7 +1164,7 @@ Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '6f26bce0b1c4e8ce0e13332f7c0083788def5fdf', 'src/third_party/openscreen/src': - Var('chromium_git') + '/openscreen' + '@' + 'efc3e5ad59d5f30529b3d1df91e6688d0bb0b80c', + Var('chromium_git') + '/openscreen' + '@' + 'd384da5a955f7594615ae49b5f624cb135d0cb01', 'src/third_party/ow2_asm': { 'packages': [ @@ -1396,7 +1396,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@009bc1a74cf5241077059f06969413d9e5ba4d32', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@002d0e113a3bba7a05d2c0205a90231b5d7a025f', 'condition': 'checkout_src_internal', },
diff --git a/WATCHLISTS b/WATCHLISTS index afa120f..dd675039 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -767,11 +767,6 @@ 'extensions/browser/api/networking_private/|'\ 'ui/chromeos/network/', }, - 'chromeos_power': { - 'filepath': 'ash/system/power/|'\ - 'chromeos/dbus/power_.*|'\ - 'chrome/browser/chromeos/power/', - }, 'chromeos_timezone': { 'filepath': 'chromeos/timezone/', }, @@ -1111,11 +1106,6 @@ 'filepath': 'third_party/libxml/' \ '|third_party/libxslt/', }, - 'linux_fonts': { - 'filepath': 'ui/gfx/font_render_params_.*|'\ - 'ui/gfx/platform_font_linux.*|'\ - 'ui/gfx/render_text_harfbuzz.*', - }, 'linux_sandboxing': { 'filepath': 'sandbox/linux/', }, @@ -1886,8 +1876,7 @@ 'WATCHLISTS': { 'about_flags': ['asvitkine+watch@chromium.org', 'jmedley+watch@chromium.org'], - 'accelerator_table': ['derat+watch@chromium.org', - 'yusukes+watch@chromium.org'], + 'accelerator_table': ['yusukes+watch@chromium.org'], 'accessibility': ['aboxhall+watch@chromium.org', 'akihiroota@chromium.org', 'aleventhal+watch@chromium.org', @@ -2221,7 +2210,6 @@ 'jlklein+watch-network@chromium.org', 'khorimoto+watch-network@chromium.org', 'stevenjb+watch-network@chromium.org'], - 'chromeos_power': ['derat+watch@chromium.org'], 'chromeos_timezone': ['alemate+watch@chromium.org'], 'chromeos_webui': ['alemate+watch@chromium.org'], 'chromevox': ['anastasi+watch@google.com'], @@ -2387,7 +2375,6 @@ 'urvang@chromium.org'], 'libxml_xslt': ['ail@google.com', 'dominicc+watchlist@chromium.org'], - 'linux_fonts': ['derat+watch@chromium.org'], 'linux_sandboxing': ['jln+watch@chromium.org'], 'linux_seccomp_bpf': ['jln+watch@chromium.org'], 'mac' : ['mac-reviews@chromium.org'], @@ -2665,8 +2652,7 @@ 'msramek+watch@chromium.org'], 'webui_backend': ['dbeam+watch-webui-backend@chromium.org'], 'windows_sandboxing': ['wfh+watch@chromium.org'], - 'x11': ['derat+watch@chromium.org', - 'sadrul@chromium.org', + 'x11': ['sadrul@chromium.org', 'yusukes+watch@chromium.org'], 'zlib': ['cblume+zlib@chromium.org'], 'zoom': ['wjmaclean@chromium.org'],
diff --git a/android_webview/browser/aw_ssl_host_state_delegate.cc b/android_webview/browser/aw_ssl_host_state_delegate.cc index 56f8ebf..a8be0ae 100644 --- a/android_webview/browser/aw_ssl_host_state_delegate.cc +++ b/android_webview/browser/aw_ssl_host_state_delegate.cc
@@ -56,7 +56,7 @@ bool AwSSLHostStateDelegate::DidHostRunInsecureContent( const std::string& host, int child_id, - InsecureContentType content_type) const { + InsecureContentType content_type) { // Intentional no-op for Android WebView. return false; } @@ -100,7 +100,7 @@ cert_policy_for_host_.erase(host); } -bool AwSSLHostStateDelegate::HasAllowException(const std::string& host) const { +bool AwSSLHostStateDelegate::HasAllowException(const std::string& host) { auto policy_iterator = cert_policy_for_host_.find(host); return policy_iterator != cert_policy_for_host_.end() && policy_iterator->second.HasAllowException();
diff --git a/android_webview/browser/aw_ssl_host_state_delegate.h b/android_webview/browser/aw_ssl_host_state_delegate.h index 79f9bb3..9776ebb7 100644 --- a/android_webview/browser/aw_ssl_host_state_delegate.h +++ b/android_webview/browser/aw_ssl_host_state_delegate.h
@@ -68,10 +68,9 @@ InsecureContentType content_type) override; // Returns whether the specified host ran insecure content. - bool DidHostRunInsecureContent( - const std::string& host, - int child_id, - InsecureContentType content_type) const override; + bool DidHostRunInsecureContent(const std::string& host, + int child_id, + InsecureContentType content_type) override; // Revokes all SSL certificate error allow exceptions made by the user for // |host|. @@ -81,7 +80,7 @@ // |host|. This does not mean that *all* certificate errors are allowed, just // that there exists an exception. To see if a particular certificate and // error combination exception is allowed, use QueryPolicy(). - bool HasAllowException(const std::string& host) const override; + bool HasAllowException(const std::string& host) override; private: // Certificate policies for each host.
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java index cfdb79ad..de05149d 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -4,6 +4,7 @@ package org.chromium.android_webview.test; +import android.support.annotation.IntDef; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.filters.SmallTest; @@ -30,9 +31,10 @@ import org.chromium.net.test.EmbeddedTestServer; import org.chromium.net.test.util.TestWebServer; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; -import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -45,6 +47,24 @@ @Rule public AwActivityTestRule mActivityTestRule = new AwActivityTestRule(); + @IntDef({CookieLifetime.OUTLIVE_THE_TEST_SEC, CookieLifetime.EXPIRE_DURING_TEST_SEC, + CookieLifetime.ALREADY_EXPIRED_SEC}) + @Retention(RetentionPolicy.SOURCE) + @interface CookieLifetime { + /** Longer than the limit of tests, so cookies will not expire during the test. */ + final int OUTLIVE_THE_TEST_SEC = 10 * 60; // 10 minutes + + /** + * Shorter than the limit of tests, so cookies may expire during the test. Be sure to wait + * at least this duration after <b>setting</b> the cookie (ex. via {@link + * AwCookieManager#setCookie(String)}). + */ + final int EXPIRE_DURING_TEST_SEC = 1; + + /** Guarantees the cookie is expired, immediately when set. */ + final int ALREADY_EXPIRED_SEC = -1; + } + private AwCookieManager mCookieManager; private TestAwContentsClient mContentsClient; private AwContents mAwContents; @@ -271,7 +291,8 @@ final String normalCookie = "cookie2=sue"; mCookieManager.setCookie(url, sessionCookie); - mCookieManager.setCookie(url, makeExpiringCookie(normalCookie, 600)); + mCookieManager.setCookie( + url, makeExpiringCookie(normalCookie, CookieLifetime.OUTLIVE_THE_TEST_SEC)); mCookieManager.removeSessionCookies(); @@ -444,7 +465,8 @@ int callCount = callback.getOnResultHelper().getCallCount(); mCookieManager.setCookie(url, sessionCookie); - mCookieManager.setCookie(url, makeExpiringCookie(normalCookie, 600)); + mCookieManager.setCookie( + url, makeExpiringCookie(normalCookie, CookieLifetime.OUTLIVE_THE_TEST_SEC)); // When there is a session cookie then it is removed. removeSessionCookiesOnUiThread(callback); @@ -471,7 +493,8 @@ final String normalCookie = "cookie2=sue"; mCookieManager.setCookie(url, sessionCookie); - mCookieManager.setCookie(url, makeExpiringCookie(normalCookie, 600)); + mCookieManager.setCookie( + url, makeExpiringCookie(normalCookie, CookieLifetime.OUTLIVE_THE_TEST_SEC)); String allCookies = mCookieManager.getCookie(url); Assert.assertTrue(allCookies.contains(sessionCookie)); Assert.assertTrue(allCookies.contains(normalCookie)); @@ -492,7 +515,8 @@ final String url = "http://www.example.com"; final String cookie = "cookie1=peter"; - mCookieManager.setCookie(url, makeExpiringCookie(cookie, -1)); + mCookieManager.setCookie( + url, makeExpiringCookie(cookie, CookieLifetime.ALREADY_EXPIRED_SEC)); assertNoCookies(url); } @@ -503,10 +527,10 @@ final String url = "http://www.example.com"; final String cookie = "cookie1=peter"; - mCookieManager.setCookie(url, makeExpiringCookieMs(cookie, 1200)); + mCookieManager.setCookie( + url, makeExpiringCookie(cookie, CookieLifetime.EXPIRE_DURING_TEST_SEC)); - // The cookie exists: - Assert.assertTrue(mCookieManager.hasCookies()); + Assert.assertTrue("Cookie should exist before expiration", mCookieManager.hasCookies()); // But eventually expires: AwActivityTestRule.pollInstrumentationThread(() -> !mCookieManager.hasCookies()); @@ -521,7 +545,8 @@ final String longCookie = "cookie2=marc"; mCookieManager.setCookie(url, sessionCookie); - mCookieManager.setCookie(url, makeExpiringCookie(longCookie, 600)); + mCookieManager.setCookie( + url, makeExpiringCookie(longCookie, CookieLifetime.OUTLIVE_THE_TEST_SEC)); String allCookies = mCookieManager.getCookie(url); Assert.assertTrue(allCookies.contains(sessionCookie)); @@ -1130,15 +1155,17 @@ foundCookieNamesSet); } - private String makeExpiringCookie(String cookie, int secondsTillExpiry) { - return makeExpiringCookieMs(cookie, secondsTillExpiry * 1000); - } - + /** + * Makes a cookie which expires {@code secondsTillExpiry} seconds after the cookie is set. Note: + * cookie expiration can only be specified to a precisiion of seconds, not to the millisecond. + * See https://tools.ietf.org/html/rfc6265#section-4.1 and + * https://tools.ietf.org/html/rfc7231#section-7.1.1.2 for details. + */ @SuppressWarnings("deprecation") - private String makeExpiringCookieMs(String cookie, int millisecondsTillExpiry) { - Date date = new Date(); - date.setTime(date.getTime() + millisecondsTillExpiry); - return cookie + "; expires=" + date.toGMTString(); + private String makeExpiringCookie(String cookie, @CookieLifetime int secondsTillExpiry) { + // Use "Max-Age" instead of "Expires", since "Max-Age" is relative to the time the cookie is + // set, rather than a call to the Date constructor when building this cookie string. + return cookie + "; Max-Age=" + secondsTillExpiry; } /**
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 9b668e2..f0eb7fff 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -1018,8 +1018,6 @@ "utility/screenshot_controller.h", "utility/transformer_util.cc", "utility/transformer_util.h", - "voice_interaction/voice_interaction_controller.cc", - "voice_interaction/voice_interaction_controller.h", "wallpaper/wallpaper_base_view.cc", "wallpaper/wallpaper_base_view.h", "wallpaper/wallpaper_controller_impl.cc", @@ -1808,7 +1806,6 @@ "tray_action/test_tray_action_client.h", "tray_action/tray_action_unittest.cc", "utility/screenshot_controller_unittest.cc", - "voice_interaction/voice_interaction_controller_unittest.cc", "wallpaper/wallpaper_controller_unittest.cc", "wallpaper/wallpaper_utils/wallpaper_color_calculator_unittest.cc", "wallpaper/wallpaper_utils/wallpaper_resizer_unittest.cc",
diff --git a/ash/OWNERS b/ash/OWNERS index 623de27..f785a7d 100644 --- a/ash/OWNERS +++ b/ash/OWNERS
@@ -1,5 +1,4 @@ afakhry@chromium.org -derat@chromium.org jamescook@chromium.org oshima@chromium.org sky@chromium.org
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc index 46d4994..f1f5749 100644 --- a/ash/accelerators/accelerator_controller_impl.cc +++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -35,6 +35,7 @@ #include "ash/public/cpp/ash_switches.h" #include "ash/public/cpp/new_window_delegate.h" #include "ash/public/cpp/notification_utils.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/public/interfaces/accessibility_controller.mojom.h" #include "ash/resources/vector_icons/vector_icons.h" #include "ash/root_window_controller.h" @@ -59,7 +60,6 @@ #include "ash/system/unified/unified_system_tray.h" #include "ash/touch/touch_hud_debug.h" #include "ash/utility/screenshot_controller.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "ash/wm/mru_window_tracker.h" #include "ash/wm/overview/overview_controller.h" #include "ash/wm/screen_pinning_controller.h" @@ -695,9 +695,8 @@ base::UserMetricsAction("VoiceInteraction.Started.Assistant")); } - switch ( - Shell::Get()->voice_interaction_controller()->allowed_state().value_or( - mojom::AssistantAllowedState::ALLOWED)) { + switch (VoiceInteractionController::Get()->allowed_state().value_or( + mojom::AssistantAllowedState::ALLOWED)) { case mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER: // Show a toast if the active user is not primary. ShowToast(kVoiceInteractionErrorToastId,
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc index c88c82b..ac953a9 100644 --- a/ash/app_list/app_list_controller_impl.cc +++ b/ash/app_list/app_list_controller_impl.cc
@@ -30,11 +30,11 @@ #include "ash/public/cpp/app_list/app_list_types.h" #include "ash/public/cpp/ash_pref_names.h" #include "ash/public/cpp/shell_window_ids.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/root_window_controller.h" #include "ash/session/session_controller_impl.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shell.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "ash/wallpaper/wallpaper_controller_impl.h" #include "ash/wm/mru_window_tracker.h" #include "ash/wm/overview/overview_controller.h" @@ -141,7 +141,7 @@ shell->AddShellObserver(this); shell->overview_controller()->AddObserver(this); keyboard::KeyboardController::Get()->AddObserver(this); - shell->voice_interaction_controller()->AddLocalObserver(this); + VoiceInteractionController::Get()->AddLocalObserver(this); shell->window_tree_host_manager()->AddObserver(this); shell->mru_window_tracker()->AddObserver(this); if (app_list_features::IsEmbeddedAssistantUIEnabled()) { @@ -1007,6 +1007,7 @@ } CloseAssistantUi(AssistantExitPoint::kLauncherClose); + model_->SetState(AppListState::kInvalidState); if (client_) client_->ViewClosing(); } @@ -1124,7 +1125,7 @@ if (!Shell::Get()->assistant_controller()->IsAssistantReady()) return false; - auto* controller = Shell::Get()->voice_interaction_controller(); + auto* controller = VoiceInteractionController::Get(); return controller->settings_enabled().value_or(false) && controller->allowed_state() == mojom::AssistantAllowedState::ALLOWED && controller->voice_interaction_state().value_or( @@ -1382,7 +1383,7 @@ } shell->mru_window_tracker()->RemoveObserver(this); shell->window_tree_host_manager()->RemoveObserver(this); - shell->voice_interaction_controller()->RemoveLocalObserver(this); + VoiceInteractionController::Get()->RemoveLocalObserver(this); keyboard::KeyboardController::Get()->RemoveObserver(this); shell->overview_controller()->RemoveObserver(this); shell->RemoveShellObserver(this);
diff --git a/ash/app_list/app_list_metrics.cc b/ash/app_list/app_list_metrics.cc index 3e29c135..a4c008aa 100644 --- a/ash/app_list/app_list_metrics.cc +++ b/ash/app_list/app_list_metrics.cc
@@ -61,12 +61,12 @@ // The UMA hisotogram that logs the action user performs on zero state // search result. constexpr char kAppListZeroStateSearchResultUserActionHistogram[] = - "Apps.AppListZeroStateSearchResultUserActionType"; + "Apps.AppList.ZeroStateSearchResultUserActionType"; // The UMA histogram that logs user's decision(remove or cancel) for zero state // search result removal confirmation. constexpr char kAppListZeroStateSearchResultRemovalHistogram[] = - "Apps.ZeroStateSearchResutRemovalDecision"; + "Apps.AppList.ZeroStateSearchResultRemovalDecision"; // The UMA histogram that logs the length of the query when user abandons // results of a queried search or recommendations of zero state(zero length
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index 9c01876..f3a9b667 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -485,6 +485,9 @@ <message name="IDS_ASH_AUTOCLICK_OPTION_DRAG_AND_DROP" desc="The tooltip text for a menu option for the automatic clicks menu that results in the next automatic click being a click and drag action."> Click and drag </message> + <message name="IDS_ASH_AUTOCLICK_OPTION_SCROLL" desc="The tooltip text for a menu option for the automatic clicks menu that launches a scroll pad for doing mousewheel scroll events."> + Scroll + </message> <message name="IDS_ASH_AUTOCLICK_OPTION_NO_ACTION" desc="The tooltip text for a menu option for the automatic clicks menu that results in pausing the automatic clicks feature without disabling it."> No action (pause) </message>
diff --git a/ash/assistant/assistant_cache_controller.cc b/ash/assistant/assistant_cache_controller.cc index 5b02ff7..ccc8b0e 100644 --- a/ash/assistant/assistant_cache_controller.cc +++ b/ash/assistant/assistant_cache_controller.cc
@@ -11,9 +11,9 @@ #include "ash/assistant/assistant_ui_controller.h" #include "ash/assistant/util/assistant_util.h" #include "ash/assistant/util/deep_link_util.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "base/metrics/field_trial_params.h" #include "base/rand_util.h" #include "chromeos/services/assistant/public/mojom/assistant.mojom.h" @@ -66,12 +66,12 @@ : assistant_controller_(assistant_controller) { UpdateConversationStarters(); assistant_controller_->AddObserver(this); - Shell::Get()->voice_interaction_controller()->AddLocalObserver(this); + VoiceInteractionController::Get()->AddLocalObserver(this); } AssistantCacheController::~AssistantCacheController() { assistant_controller_->RemoveObserver(this); - Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this); + VoiceInteractionController::Get()->RemoveLocalObserver(this); } void AssistantCacheController::AddModelObserver( @@ -132,8 +132,7 @@ // If enabled, always show the "What's on my screen?" conversation starter. if (kWhatsOnMyScreenChipEnabled.Get() && - Shell::Get()->voice_interaction_controller()->context_enabled().value_or( - false)) { + VoiceInteractionController::Get()->context_enabled().value_or(false)) { AddConversationStarter(IDS_ASH_ASSISTANT_CHIP_WHATS_ON_MY_SCREEN, assistant::util::CreateWhatsOnMyScreenDeepLink()); }
diff --git a/ash/assistant/assistant_controller.cc b/ash/assistant/assistant_controller.cc index 9621c098..436d1bc 100644 --- a/ash/assistant/assistant_controller.cc +++ b/ash/assistant/assistant_controller.cc
@@ -12,10 +12,10 @@ #include "ash/public/cpp/android_intent_helper.h" #include "ash/public/cpp/ash_pref_names.h" #include "ash/public/cpp/new_window_delegate.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/utility/screenshot_controller.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "base/bind.h" #include "base/memory/scoped_refptr.h" #include "components/prefs/pref_registry_simple.h" @@ -42,7 +42,7 @@ assistant_ui_controller_(this), view_delegate_(this), weak_factory_(this) { - Shell::Get()->voice_interaction_controller()->AddLocalObserver(this); + VoiceInteractionController::Get()->AddLocalObserver(this); chromeos::CrasAudioHandler::Get()->AddAudioObserver(this); AddObserver(this); @@ -54,7 +54,7 @@ chromeos::CrasAudioHandler::Get()->RemoveAudioObserver(this); Shell::Get()->accessibility_controller()->RemoveObserver(this); - Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this); + VoiceInteractionController::Get()->RemoveLocalObserver(this); RemoveObserver(this); } @@ -118,7 +118,7 @@ void AssistantController::StartSpeakerIdEnrollmentFlow() { mojom::ConsentStatus consent_status = - Shell::Get()->voice_interaction_controller()->consent_status().value_or( + VoiceInteractionController::Get()->consent_status().value_or( mojom::ConsentStatus::kUnknown); if (consent_status == mojom::ConsentStatus::kActivityControlAccepted) { // If activity control has been accepted, launch the enrollment flow.
diff --git a/ash/assistant/assistant_interaction_controller.cc b/ash/assistant/assistant_interaction_controller.cc index 57c2ba4..0a136a4 100644 --- a/ash/assistant/assistant_interaction_controller.cc +++ b/ash/assistant/assistant_interaction_controller.cc
@@ -21,11 +21,11 @@ #include "ash/assistant/util/histogram_util.h" #include "ash/public/cpp/app_list/app_list_features.h" #include "ash/public/cpp/ash_pref_names.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/public/interfaces/voice_interaction_controller.mojom.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "base/bind.h" #include "base/optional.h" @@ -694,7 +694,7 @@ assistant_controller_->ui_controller()->model()->visibility()); const bool launch_with_mic_open = - Shell::Get()->voice_interaction_controller()->launch_with_mic_open(); + VoiceInteractionController::Get()->launch_with_mic_open(); const bool prefer_voice = launch_with_mic_open || IsTabletMode(); // We don't explicitly start a new voice interaction if the entry point
diff --git a/ash/assistant/assistant_notification_controller.cc b/ash/assistant/assistant_notification_controller.cc index 19dcc7a..c837890 100644 --- a/ash/assistant/assistant_notification_controller.cc +++ b/ash/assistant/assistant_notification_controller.cc
@@ -11,10 +11,10 @@ #include "ash/assistant/util/deep_link_util.h" #include "ash/public/cpp/notification_utils.h" #include "ash/public/cpp/vector_icons/vector_icons.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/public/interfaces/voice_interaction_controller.mojom.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/l10n/l10n_util.h" #include "ui/message_center/message_center.h" @@ -200,7 +200,7 @@ void AssistantNotificationController::OnNotificationAdded( const AssistantNotification* notification) { // Do not show system notifications if the setting is disabled. - if (!Shell::Get()->voice_interaction_controller()->notification_enabled()) + if (!VoiceInteractionController::Get()->notification_enabled()) return; // We only show system notifications in the Message Center. @@ -214,7 +214,7 @@ void AssistantNotificationController::OnNotificationUpdated( const AssistantNotification* notification) { // Do not show system notifications if the setting is disabled. - if (!Shell::Get()->voice_interaction_controller()->notification_enabled()) + if (!VoiceInteractionController::Get()->notification_enabled()) return; // If the notification that was updated is *not* a system notification, we
diff --git a/ash/assistant/assistant_screen_context_controller.cc b/ash/assistant/assistant_screen_context_controller.cc index bbc4bcf7..7d889e65 100644 --- a/ash/assistant/assistant_screen_context_controller.cc +++ b/ash/assistant/assistant_screen_context_controller.cc
@@ -12,9 +12,7 @@ #include "ash/assistant/assistant_ui_controller.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/public/cpp/window_properties.h" -#include "ash/public/interfaces/voice_interaction_controller.mojom.h" #include "ash/shell.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "ash/wm/mru_window_tracker.h" #include "base/bind.h" #include "base/memory/scoped_refptr.h"
diff --git a/ash/assistant/assistant_setup_controller.cc b/ash/assistant/assistant_setup_controller.cc index 8956dd4e..2d5caf0 100644 --- a/ash/assistant/assistant_setup_controller.cc +++ b/ash/assistant/assistant_setup_controller.cc
@@ -8,9 +8,9 @@ #include "ash/assistant/assistant_ui_controller.h" #include "ash/assistant/util/deep_link_util.h" #include "ash/assistant/util/i18n_util.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/public/interfaces/voice_interaction_controller.mojom.h" #include "ash/shell.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "chromeos/services/assistant/public/features.h" @@ -58,7 +58,7 @@ void AssistantSetupController::OnOptInButtonPressed() { mojom::ConsentStatus consent_status = - Shell::Get()->voice_interaction_controller()->consent_status().value_or( + VoiceInteractionController::Get()->consent_status().value_or( mojom::ConsentStatus::kUnknown); if (consent_status == mojom::ConsentStatus::kUnauthorized) {
diff --git a/ash/assistant/assistant_ui_controller.cc b/ash/assistant/assistant_ui_controller.cc index 207cca6c..dbf3641 100644 --- a/ash/assistant/assistant_ui_controller.cc +++ b/ash/assistant/assistant_ui_controller.cc
@@ -14,12 +14,12 @@ #include "ash/assistant/util/histogram_util.h" #include "ash/multi_user/multi_user_window_manager_impl.h" #include "ash/public/cpp/app_list/app_list_features.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" #include "ash/system/toast/toast_data.h" #include "ash/system/toast/toast_manager.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "base/bind.h" #include "base/optional.h" #include "chromeos/services/assistant/public/mojom/assistant.mojom.h" @@ -276,7 +276,7 @@ AssistantVisibility old_visibility, base::Optional<AssistantEntryPoint> entry_point, base::Optional<AssistantExitPoint> exit_point) { - Shell::Get()->voice_interaction_controller()->NotifyStatusChanged( + VoiceInteractionController::Get()->NotifyStatusChanged( new_visibility == AssistantVisibility::kVisible ? mojom::VoiceInteractionState::RUNNING : mojom::VoiceInteractionState::STOPPED); @@ -352,8 +352,7 @@ } void AssistantUiController::ShowUi(AssistantEntryPoint entry_point) { - auto* voice_interaction_controller = - Shell::Get()->voice_interaction_controller(); + auto* voice_interaction_controller = VoiceInteractionController::Get(); if (!voice_interaction_controller->settings_enabled().value_or(false) || voice_interaction_controller->locked_full_screen_enabled().value_or( @@ -362,7 +361,7 @@ } // TODO(dmblack): Show a more helpful message to the user. - if (Shell::Get()->voice_interaction_controller()->voice_interaction_state() == + if (VoiceInteractionController::Get()->voice_interaction_state() == mojom::VoiceInteractionState::NOT_READY) { ShowToast(kUnboundServiceToastId, IDS_ASH_ASSISTANT_ERROR_GENERIC); return;
diff --git a/ash/assistant/assistant_view_delegate_impl.cc b/ash/assistant/assistant_view_delegate_impl.cc index cf4cbec..a264a4a 100644 --- a/ash/assistant/assistant_view_delegate_impl.cc +++ b/ash/assistant/assistant_view_delegate_impl.cc
@@ -9,8 +9,8 @@ #include "ash/assistant/assistant_controller_observer.h" #include "ash/assistant/assistant_interaction_controller.h" #include "ash/assistant/assistant_notification_controller.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/shell.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" namespace ash { @@ -93,12 +93,12 @@ void AssistantViewDelegateImpl::AddVoiceInteractionControllerObserver( DefaultVoiceInteractionObserver* observer) { - Shell::Get()->voice_interaction_controller()->AddLocalObserver(observer); + VoiceInteractionController::Get()->AddLocalObserver(observer); } void AssistantViewDelegateImpl::RemoveVoiceInteractionControllerObserver( DefaultVoiceInteractionObserver* observer) { - Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(observer); + VoiceInteractionController::Get()->RemoveLocalObserver(observer); } CaptionBarDelegate* AssistantViewDelegateImpl::GetCaptionBarDelegate() { @@ -112,10 +112,8 @@ } mojom::ConsentStatus AssistantViewDelegateImpl::GetConsentStatus() const { - return Shell::Get() - ->voice_interaction_controller() - ->consent_status() - .value_or(mojom::ConsentStatus::kUnknown); + return VoiceInteractionController::Get()->consent_status().value_or( + mojom::ConsentStatus::kUnknown); } ::wm::CursorManager* AssistantViewDelegateImpl::GetCursorManager() { @@ -132,7 +130,7 @@ } bool AssistantViewDelegateImpl::IsLaunchWithMicOpen() const { - return Shell::Get()->voice_interaction_controller()->launch_with_mic_open(); + return VoiceInteractionController::Get()->launch_with_mic_open(); } bool AssistantViewDelegateImpl::IsTabletMode() const {
diff --git a/ash/assistant/ui/DEPS b/ash/assistant/ui/DEPS index d93a1786..bea464e 100644 --- a/ash/assistant/ui/DEPS +++ b/ash/assistant/ui/DEPS
@@ -24,7 +24,6 @@ "+ash/assistant/assistant_ui_controller.h", "+ash/shell.h", "+ash/test/ash_test_base.h", - "+ash/voice_interaction/voice_interaction_controller.h", "+ash/wm/tablet_mode/tablet_mode_controller.h", "+base/test/scoped_feature_list.h", "+chromeos/constants/chromeos_switches.h",
diff --git a/ash/assistant/ui/assistant_container_view_unittest.cc b/ash/assistant/ui/assistant_container_view_unittest.cc index be61cb75..e096c463 100644 --- a/ash/assistant/ui/assistant_container_view_unittest.cc +++ b/ash/assistant/ui/assistant_container_view_unittest.cc
@@ -8,9 +8,9 @@ #include "ash/assistant/assistant_controller.h" #include "ash/assistant/assistant_ui_controller.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "base/macros.h" #include "base/test/scoped_feature_list.h" @@ -38,7 +38,7 @@ AshTestBase::SetUp(); // Enable Assistant in settings. - Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true); + VoiceInteractionController::Get()->NotifySettingsEnabled(true); // Cache controller. controller_ = Shell::Get()->assistant_controller(); @@ -52,7 +52,7 @@ // After mocks are set up our Assistant service is ready for use. Indicate // this by changing status from NOT_READY to STOPPED. - Shell::Get()->voice_interaction_controller()->NotifyStatusChanged( + VoiceInteractionController::Get()->NotifyStatusChanged( mojom::VoiceInteractionState::STOPPED); }
diff --git a/ash/autoclick/autoclick_controller.cc b/ash/autoclick/autoclick_controller.cc index b024d50..5b34142 100644 --- a/ash/autoclick/autoclick_controller.cc +++ b/ash/autoclick/autoclick_controller.cc
@@ -420,8 +420,10 @@ base::RecordAction( base::UserMetricsAction("Accessibility.Autoclick.DragAndDrop")); return; + case mojom::AutoclickEventType::kScroll: + // Scroll users actions will be recorded from AutoclickScrollView. case mojom::AutoclickEventType::kNoAction: - // No action shouldn't have a UserAction, so we return null. + // No action shouldn't have a UserAction, so we return. return; } }
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc index cb6bf2d2..a87422a 100644 --- a/ash/mojo_interface_factory.cc +++ b/ash/mojo_interface_factory.cc
@@ -24,6 +24,7 @@ #include "ash/media/media_controller.h" #include "ash/public/cpp/ash_features.h" #include "ash/public/cpp/ash_switches.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/shell.h" #include "ash/shell_delegate.h" #include "ash/shutdown_controller.h" @@ -32,7 +33,6 @@ #include "ash/system/network/vpn_list.h" #include "ash/system/night_light/night_light_controller.h" #include "ash/tray_action/tray_action.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "base/bind.h" #include "base/command_line.h" #include "base/lazy_instance.h" @@ -146,7 +146,7 @@ void BindVoiceInteractionControllerRequestOnMainThread( mojom::VoiceInteractionControllerRequest request) { - Shell::Get()->voice_interaction_controller()->BindRequest(std::move(request)); + VoiceInteractionController::Get()->BindRequest(std::move(request)); } void BindVpnListRequestOnMainThread(mojom::VpnListRequest request) {
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn index b55a905c..1888d96 100644 --- a/ash/public/cpp/BUILD.gn +++ b/ash/public/cpp/BUILD.gn
@@ -157,6 +157,8 @@ "tablet_mode_toggle_observer.h", "touch_uma.cc", "touch_uma.h", + "voice_interaction_controller.cc", + "voice_interaction_controller.h", "wallpaper_controller.cc", "wallpaper_controller.h", "wallpaper_controller_client.h", @@ -256,6 +258,7 @@ "power_utils_unittest.cc", "rounded_corner_decorator_unittest.cc", "shelf_model_unittest.cc", + "voice_interaction_controller_unittest.cc", ] deps = [
diff --git a/ash/public/cpp/voice_interaction_controller.cc b/ash/public/cpp/voice_interaction_controller.cc new file mode 100644 index 0000000..2f12873 --- /dev/null +++ b/ash/public/cpp/voice_interaction_controller.cc
@@ -0,0 +1,217 @@ +// Copyright 2019 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 "ash/public/cpp/voice_interaction_controller.h" + +#include <utility> + +#include "chromeos/constants/chromeos_switches.h" + +namespace ash { +namespace { +VoiceInteractionController* g_voice_interaction_cotroller = nullptr; +} + +// static +VoiceInteractionController* VoiceInteractionController::Get() { + return g_voice_interaction_cotroller; +} + +VoiceInteractionController::VoiceInteractionController() { + DCHECK(!g_voice_interaction_cotroller); + g_voice_interaction_cotroller = this; + if (chromeos::switches::IsAssistantEnabled()) + voice_interaction_state_ = mojom::VoiceInteractionState::NOT_READY; +} + +VoiceInteractionController::~VoiceInteractionController() { + DCHECK_EQ(g_voice_interaction_cotroller, this); + g_voice_interaction_cotroller = nullptr; +} + +void VoiceInteractionController::BindRequest( + mojom::VoiceInteractionControllerRequest request) { + bindings_.AddBinding(this, std::move(request)); +} + +void VoiceInteractionController::NotifyStatusChanged( + mojom::VoiceInteractionState state) { + if (voice_interaction_state_ == state) + return; + + voice_interaction_state_ = state; + observers_.ForAllPtrs([state](auto* observer) { + observer->OnVoiceInteractionStatusChanged(state); + }); + for (auto& observer : local_observers_) + observer.OnVoiceInteractionStatusChanged(state); +} + +void VoiceInteractionController::NotifySettingsEnabled(bool enabled) { + if (settings_enabled_.has_value() && settings_enabled_.value() == enabled) + return; + + settings_enabled_ = enabled; + observers_.ForAllPtrs([enabled](auto* observer) { + observer->OnVoiceInteractionSettingsEnabled(enabled); + }); + for (auto& observer : local_observers_) + observer.OnVoiceInteractionSettingsEnabled(enabled); +} + +void VoiceInteractionController::NotifyContextEnabled(bool enabled) { + if (context_enabled_.has_value() && context_enabled_.value() == enabled) + return; + + context_enabled_ = enabled; + observers_.ForAllPtrs([enabled](auto* observer) { + observer->OnVoiceInteractionContextEnabled(enabled); + }); + for (auto& observer : local_observers_) + observer.OnVoiceInteractionContextEnabled(enabled); +} + +void VoiceInteractionController::NotifyHotwordEnabled(bool enabled) { + if (hotword_enabled_.has_value() && hotword_enabled_.value() == enabled) + return; + + hotword_enabled_ = enabled; + observers_.ForAllPtrs([enabled](auto* observer) { + observer->OnVoiceInteractionHotwordEnabled(enabled); + }); + for (auto& observer : local_observers_) + observer.OnVoiceInteractionHotwordEnabled(enabled); +} + +void VoiceInteractionController::NotifyHotwordAlwaysOn(bool always_on) { + if (hotword_always_on_.has_value() && hotword_always_on_.value() == always_on) + return; + + hotword_always_on_ = always_on; + observers_.ForAllPtrs([always_on](auto* observer) { + observer->OnVoiceInteractionHotwordAlwaysOn(always_on); + }); + for (auto& observer : local_observers_) + observer.OnVoiceInteractionHotwordAlwaysOn(always_on); +} + +void VoiceInteractionController::NotifyConsentStatus( + mojom::ConsentStatus consent_status) { + if (consent_status_.has_value() && consent_status_.value() == consent_status) + return; + + consent_status_ = consent_status; + observers_.ForAllPtrs([consent_status](auto* observer) { + observer->OnVoiceInteractionConsentStatusUpdated(consent_status); + }); + for (auto& observer : local_observers_) + observer.OnVoiceInteractionConsentStatusUpdated(consent_status); +} + +void VoiceInteractionController::NotifyFeatureAllowed( + mojom::AssistantAllowedState state) { + if (allowed_state_ == state) + return; + + allowed_state_ = state; + observers_.ForAllPtrs([state](auto* observer) { + observer->OnAssistantFeatureAllowedChanged(state); + }); + for (auto& observer : local_observers_) + observer.OnAssistantFeatureAllowedChanged(state); +} + +void VoiceInteractionController::NotifyNotificationEnabled(bool enabled) { + notification_enabled_ = enabled; +} + +void VoiceInteractionController::NotifyLocaleChanged( + const std::string& locale) { + if (locale_ == locale) + return; + + locale_ = locale; + observers_.ForAllPtrs( + [locale](auto* observer) { observer->OnLocaleChanged(locale); }); + for (auto& observer : local_observers_) + observer.OnLocaleChanged(locale); +} + +void VoiceInteractionController::NotifyLaunchWithMicOpen( + bool launch_with_mic_open) { + launch_with_mic_open_ = launch_with_mic_open; +} + +void VoiceInteractionController::NotifyArcPlayStoreEnabledChanged( + bool enabled) { + if (arc_play_store_enabled_ == enabled) + return; + + arc_play_store_enabled_ = enabled; + + observers_.ForAllPtrs([enabled](auto* observer) { + observer->OnArcPlayStoreEnabledChanged(enabled); + }); + for (auto& observer : local_observers_) + observer.OnArcPlayStoreEnabledChanged(enabled); +} + +void VoiceInteractionController::NotifyLockedFullScreenStateChanged( + bool enabled) { + if (locked_full_screen_enabled_ == enabled) + return; + + locked_full_screen_enabled_ = enabled; + + observers_.ForAllPtrs([enabled](auto* observer) { + observer->OnLockedFullScreenStateChanged(enabled); + }); + for (auto& observer : local_observers_) + observer.OnLockedFullScreenStateChanged(enabled); +} + +void VoiceInteractionController::AddObserver( + mojom::VoiceInteractionObserverPtr observer) { + InitObserver(observer.get()); + observers_.AddPtr(std::move(observer)); +} + +void VoiceInteractionController::AddLocalObserver( + DefaultVoiceInteractionObserver* observer) { + InitObserver(observer); + local_observers_.AddObserver(observer); +} + +void VoiceInteractionController::RemoveLocalObserver( + DefaultVoiceInteractionObserver* observer) { + local_observers_.RemoveObserver(observer); +} + +void VoiceInteractionController::InitObserver( + mojom::VoiceInteractionObserver* observer) { + if (voice_interaction_state_.has_value()) + observer->OnVoiceInteractionStatusChanged(voice_interaction_state_.value()); + if (settings_enabled_.has_value()) + observer->OnVoiceInteractionSettingsEnabled(settings_enabled_.value()); + if (context_enabled_.has_value()) + observer->OnVoiceInteractionContextEnabled(context_enabled_.value()); + if (hotword_enabled_.has_value()) + observer->OnVoiceInteractionHotwordEnabled(hotword_enabled_.value()); + if (consent_status_.has_value()) + observer->OnVoiceInteractionConsentStatusUpdated(consent_status_.value()); + if (hotword_always_on_.has_value()) + observer->OnVoiceInteractionHotwordAlwaysOn(hotword_always_on_.value()); + if (allowed_state_.has_value()) + observer->OnAssistantFeatureAllowedChanged(allowed_state_.value()); + if (locale_.has_value()) + observer->OnLocaleChanged(locale_.value()); + if (arc_play_store_enabled_.has_value()) + observer->OnArcPlayStoreEnabledChanged(arc_play_store_enabled_.value()); +} + +void VoiceInteractionController::FlushForTesting() { + observers_.FlushForTesting(); +} + +} // namespace ash
diff --git a/ash/public/cpp/voice_interaction_controller.h b/ash/public/cpp/voice_interaction_controller.h new file mode 100644 index 0000000..bc87a0cf --- /dev/null +++ b/ash/public/cpp/voice_interaction_controller.h
@@ -0,0 +1,102 @@ +// Copyright 2019 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 ASH_PUBLIC_CPP_VOICE_INTERACTION_CONTROLLER_H_ +#define ASH_PUBLIC_CPP_VOICE_INTERACTION_CONTROLLER_H_ + +#include <string> + +#include "ash/public/cpp/ash_public_export.h" +#include "ash/public/cpp/assistant/assistant_state_base.h" +#include "ash/public/cpp/assistant/default_voice_interaction_observer.h" +#include "ash/public/interfaces/voice_interaction_controller.mojom.h" +#include "base/observer_list.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/bindings/interface_ptr_set.h" + +namespace ash { + +class ASH_PUBLIC_EXPORT VoiceInteractionController + : public mojom::VoiceInteractionController, + public AssistantStateBase { + public: + static VoiceInteractionController* Get(); + + VoiceInteractionController(); + ~VoiceInteractionController() override; + + void BindRequest(mojom::VoiceInteractionControllerRequest request); + + // Called when the voice interaction state is changed. + virtual void NotifyStatusChanged(mojom::VoiceInteractionState state); + + // Called when the voice interaction settings is enabled/disabled. + virtual void NotifySettingsEnabled(bool enabled); + + // Called when the voice interaction context is enabled/disabled. + // If context is enabled the screenshot will be passed in voice + // interaction session. + virtual void NotifyContextEnabled(bool enabled); + + // Called when the hotword listening is enabled/disabled. + virtual void NotifyHotwordEnabled(bool enabled); + + // Called when the hotword is set to always on/only with power source. + virtual void NotifyHotwordAlwaysOn(bool always_on); + + // Called when the consent status is obtained from the server. + virtual void NotifyConsentStatus(mojom::ConsentStatus consent_status); + + // Notify if voice interaction feature is allowed or not. e.g. not allowed + // if disabled by policy. + virtual void NotifyFeatureAllowed(mojom::AssistantAllowedState state); + + // Called when the notification is enabled/disabled. + virtual void NotifyNotificationEnabled(bool enabled); + + // Called when the locale is changed. + virtual void NotifyLocaleChanged(const std::string& locale); + + // Called when the launch with mic open state is changed. + virtual void NotifyLaunchWithMicOpen(bool launch_with_mic_open); + + // Called when Google Play Store is enabled/disabled. + virtual void NotifyArcPlayStoreEnabledChanged(bool enabled); + + // Called when locked full screen state is enabled/disabled. + virtual void NotifyLockedFullScreenStateChanged(bool enabled); + + // ash::mojom::VoiceInteractionController: + void AddObserver(mojom::VoiceInteractionObserverPtr observer) override; + + // Adding local observers in the same process. + void AddLocalObserver(DefaultVoiceInteractionObserver* observer); + void RemoveLocalObserver(DefaultVoiceInteractionObserver* observer); + void InitObserver(mojom::VoiceInteractionObserver* observer); + + bool notification_enabled() const { return notification_enabled_; } + + bool launch_with_mic_open() const { return launch_with_mic_open_; } + + void FlushForTesting(); + + private: + // Whether notification is enabled. + bool notification_enabled_ = false; + + // Whether the Assistant should launch with mic open; + bool launch_with_mic_open_ = false; + + mojo::BindingSet<mojom::VoiceInteractionController> bindings_; + + mojo::InterfacePtrSet<mojom::VoiceInteractionObserver> observers_; + + base::ObserverList<DefaultVoiceInteractionObserver> local_observers_; + + DISALLOW_COPY_AND_ASSIGN(VoiceInteractionController); +}; + +} // namespace ash + +#endif // ASH_PUBLIC_CPP_VOICE_INTERACTION_CONTROLLER_H_
diff --git a/ash/public/cpp/voice_interaction_controller_unittest.cc b/ash/public/cpp/voice_interaction_controller_unittest.cc new file mode 100644 index 0000000..261fcd4 --- /dev/null +++ b/ash/public/cpp/voice_interaction_controller_unittest.cc
@@ -0,0 +1,174 @@ +// Copyright 2019 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 "ash/public/cpp/voice_interaction_controller.h" + +#include <memory> +#include <utility> + +#include "ash/public/interfaces/voice_interaction_controller.mojom.h" +#include "base/test/scoped_task_environment.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace ash { +namespace { + +class TestVoiceInteractionObserver : public mojom::VoiceInteractionObserver { + public: + TestVoiceInteractionObserver() : voice_interaction_binding_(this) {} + + ~TestVoiceInteractionObserver() override = default; + + // mojom::VoiceInteractionObserver overrides: + void OnVoiceInteractionStatusChanged( + mojom::VoiceInteractionState state) override { + state_ = state; + } + void OnVoiceInteractionSettingsEnabled(bool enabled) override { + settings_enabled_ = enabled; + } + void OnVoiceInteractionContextEnabled(bool enabled) override { + context_enabled_ = enabled; + } + void OnVoiceInteractionHotwordAlwaysOn(bool always_on) override { + hotword_always_on_ = always_on; + } + void OnVoiceInteractionHotwordEnabled(bool enabled) override { + hotword_enabled_ = enabled; + } + void OnVoiceInteractionConsentStatusUpdated( + mojom::ConsentStatus consent_status) override { + consent_status_ = consent_status; + } + void OnAssistantFeatureAllowedChanged( + mojom::AssistantAllowedState state) override {} + void OnLocaleChanged(const std::string& locale) override {} + void OnArcPlayStoreEnabledChanged(bool enabled) override { + arc_play_store_enabled_ = enabled; + } + void OnLockedFullScreenStateChanged(bool enabled) override {} + + mojom::VoiceInteractionState voice_interaction_state() const { + return state_; + } + bool settings_enabled() const { return settings_enabled_; } + bool context_enabled() const { return context_enabled_; } + bool hotword_always_on() const { return hotword_always_on_; } + bool hotword_enabled() const { return hotword_enabled_; } + bool arc_play_store_enabled() const { return arc_play_store_enabled_; } + mojom::ConsentStatus consent_status() const { return consent_status_; } + + void SetVoiceInteractionController(VoiceInteractionController* controller) { + mojom::VoiceInteractionObserverPtr ptr; + voice_interaction_binding_.Bind(mojo::MakeRequest(&ptr)); + controller->AddObserver(std::move(ptr)); + } + + private: + mojom::VoiceInteractionState state_ = mojom::VoiceInteractionState::STOPPED; + bool settings_enabled_ = false; + bool context_enabled_ = false; + bool hotword_always_on_ = false; + bool hotword_enabled_ = false; + bool arc_play_store_enabled_ = false; + mojom::ConsentStatus consent_status_ = mojom::ConsentStatus::kUnknown; + + mojo::Binding<mojom::VoiceInteractionObserver> voice_interaction_binding_; + + DISALLOW_COPY_AND_ASSIGN(TestVoiceInteractionObserver); +}; + +class VoiceInteractionControllerTest : public testing::Test { + public: + VoiceInteractionControllerTest() + : scoped_task_environment_( + base::test::ScopedTaskEnvironment::MainThreadType::UI) {} + ~VoiceInteractionControllerTest() override = default; + + void SetUp() override { + controller_ = std::make_unique<VoiceInteractionController>(); + observer_ = std::make_unique<TestVoiceInteractionObserver>(); + observer_->SetVoiceInteractionController(controller()); + } + + void TearDown() override { observer_.reset(); } + + protected: + VoiceInteractionController* controller() { return controller_.get(); } + + TestVoiceInteractionObserver* observer() { return observer_.get(); } + + private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + std::unique_ptr<VoiceInteractionController> controller_; + std::unique_ptr<TestVoiceInteractionObserver> observer_; + + DISALLOW_COPY_AND_ASSIGN(VoiceInteractionControllerTest); +}; + +} // namespace + +TEST_F(VoiceInteractionControllerTest, NotifyStatusChanged) { + controller()->NotifyStatusChanged(mojom::VoiceInteractionState::RUNNING); + controller()->FlushForTesting(); + + // The cached state should be updated. + EXPECT_EQ(mojom::VoiceInteractionState::RUNNING, + controller()->voice_interaction_state()); + // The observers should be notified. + EXPECT_EQ(mojom::VoiceInteractionState::RUNNING, + observer()->voice_interaction_state()); +} + +TEST_F(VoiceInteractionControllerTest, NotifySettingsEnabled) { + controller()->NotifySettingsEnabled(true); + controller()->FlushForTesting(); + // The cached state should be updated. + EXPECT_TRUE(controller()->settings_enabled()); + // The observers should be notified. + EXPECT_TRUE(observer()->settings_enabled()); +} + +TEST_F(VoiceInteractionControllerTest, NotifyContextEnabled) { + controller()->NotifyContextEnabled(true); + controller()->FlushForTesting(); + // The observers should be notified. + EXPECT_TRUE(observer()->context_enabled()); +} + +TEST_F(VoiceInteractionControllerTest, NotifyHotwordAlwaysOn) { + controller()->NotifyHotwordAlwaysOn(true); + controller()->FlushForTesting(); + // The observers should be notified. + EXPECT_TRUE(observer()->hotword_always_on()); +} + +TEST_F(VoiceInteractionControllerTest, NotifyHotwordEnabled) { + controller()->NotifyHotwordEnabled(true); + controller()->FlushForTesting(); + // The observers should be notified. + EXPECT_TRUE(observer()->hotword_enabled()); +} + +TEST_F(VoiceInteractionControllerTest, NotifyConsentStatus) { + controller()->NotifyConsentStatus( + mojom::ConsentStatus::kActivityControlAccepted); + controller()->FlushForTesting(); + // The cached state should be updated. + EXPECT_TRUE(controller()->consent_status() == + mojom::ConsentStatus::kActivityControlAccepted); + // The observers should be notified. + EXPECT_TRUE(observer()->consent_status() == + mojom::ConsentStatus::kActivityControlAccepted); +} + +TEST_F(VoiceInteractionControllerTest, NotifyArcPlayStoreEnabledChanged) { + controller()->NotifyArcPlayStoreEnabledChanged(true); + controller()->FlushForTesting(); + // The observers should be notified. + EXPECT_TRUE(observer()->arc_play_store_enabled()); +} + +} // namespace ash
diff --git a/ash/public/interfaces/accessibility_controller_enums.mojom b/ash/public/interfaces/accessibility_controller_enums.mojom index 9c23483..8254baa1 100644 --- a/ash/public/interfaces/accessibility_controller_enums.mojom +++ b/ash/public/interfaces/accessibility_controller_enums.mojom
@@ -84,22 +84,24 @@ // should be added at the end. enum AutoclickEventType { // Perform a left click. - kLeftClick, + kLeftClick = 0, // Perform a right click. - kRightClick, + kRightClick = 1, // Perform a drag and drop, i.e. click down at the first dwell, and up at the // second dwell. - kDragAndDrop, + kDragAndDrop = 2, // Perform a double-click. - kDoubleClick, + kDoubleClick = 3, // A non-action, i.e. nothing will happen at the end of the dwell time. - kNoAction, + kNoAction = 4, - // TODO(katie): Add scroll. + // A mousewheel scroll action. An additional menu will be shown for the user + // to pick whether they want to scroll up/down/left/right. + kScroll = 5, }; // The Automatic Clicks feature's on-screen menu display location. These values
diff --git a/ash/public/interfaces/voice_interaction_controller.mojom b/ash/public/interfaces/voice_interaction_controller.mojom index 7b3136da..efc163d 100644 --- a/ash/public/interfaces/voice_interaction_controller.mojom +++ b/ash/public/interfaces/voice_interaction_controller.mojom
@@ -88,45 +88,6 @@ // Interface for ash client (Chrome) to connect to the voice interaction // controller, which notifies changes of voice interaction related flags. interface VoiceInteractionController { - // Called when the voice interaction state is changed. - NotifyStatusChanged(VoiceInteractionState state); - - // Called when the voice interaction settings is enabled/disabled. - NotifySettingsEnabled(bool enabled); - - // Called when the voice interaction context is enabled/disabled. - // If context is enabled the screenshot will be passed in voice - // interaction session. - NotifyContextEnabled(bool enabled); - - // Called when the hotword listening is enabled/disabled. - NotifyHotwordEnabled(bool enabled); - - // Called when the hotword is set to always on/only with power source. - NotifyHotwordAlwaysOn(bool enabled); - - // Called when the consent status is obtained from the server. - NotifyConsentStatus(ConsentStatus consent_status); - - // Notify if voice interaction feature is allowed or not. e.g. not allowed - // if disabled by policy. - NotifyFeatureAllowed(AssistantAllowedState state); - - // Called when the notification is enabled/disabled. - NotifyNotificationEnabled(bool enabled); - - // Called when the locale is changed. - NotifyLocaleChanged(string locale); - - // Called when the launch with mic open state is changed. - NotifyLaunchWithMicOpen(bool launch_with_mic_open); - - // Called when Google Play Store is enabled/disabled. - NotifyArcPlayStoreEnabledChanged(bool enabled); - - // Called when locked full screen state is enabled/disabled. - NotifyLockedFullScreenStateChanged(bool enabled); - // Add an observer. AddObserver(VoiceInteractionObserver observer); };
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn index 8f116a99..258be518 100644 --- a/ash/resources/vector_icons/BUILD.gn +++ b/ash/resources/vector_icons/BUILD.gn
@@ -21,6 +21,7 @@ "autoclick_position_bottom_right.icon", "autoclick_position_top_left.icon", "autoclick_position_top_right.icon", + "autoclick_scroll.icon", "captive_portal.icon", "check_circle.icon", "desks_close_desk_button.icon",
diff --git a/ash/resources/vector_icons/autoclick_scroll.icon b/ash/resources/vector_icons/autoclick_scroll.icon new file mode 100644 index 0000000..0290114 --- /dev/null +++ b/ash/resources/vector_icons/autoclick_scroll.icon
@@ -0,0 +1,38 @@ +// Copyright 2019 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. + +CANVAS_DIMENSIONS, 20, +R_MOVE_TO, 9, 11, +R_H_LINE_TO, -4.71f, +R_LINE_TO, 1.95f, 1.87f, +R_LINE_TO, -1.15f, 1.13f, +R_LINE_TO, -4.09f, -4, +R_LINE_TO, 4.09f, -4, +R_LINE_TO, 1.15f, 1.13f, +R_LINE_TO, -1.87f, 1.87f, +R_H_LINE_TO, 4.63f, +R_V_LINE_TO, -4.71f, +R_LINE_TO, -1.87f, 1.95f, +R_LINE_TO, -1.13f, -1.15f, +R_LINE_TO, 4, -4.09f, +R_LINE_TO, 4, 4.09f, +R_LINE_TO, -1.13f, 1.15f, +R_LINE_TO, -1.87f, -1.87f, +R_V_LINE_TO, 4.63f, +R_H_LINE_TO, 4.71f, +R_LINE_TO, -1.95f, -1.87f, +R_LINE_TO, 1.15f, -1.13f, +R_LINE_TO, 4.09f, 4, +R_LINE_TO, -4.09f, 4, +R_LINE_TO, -1.15f, -1.13f, +R_LINE_TO, 1.87f, -1.87f, +R_H_LINE_TO, -4.63f, +R_V_LINE_TO, 4.71f, +R_LINE_TO, 1.87f, -1.95f, +R_LINE_TO, 1.13f, 1.15f, +R_LINE_TO, -4, 4.09f, +R_LINE_TO, -4, -4.09f, +R_LINE_TO, 1.13f, -1.15f, +R_LINE_TO, 1.87f, 1.87f, +CLOSE
diff --git a/ash/shelf/app_list_button_controller.cc b/ash/shelf/app_list_button_controller.cc index 705ea72..bd9ba84 100644 --- a/ash/shelf/app_list_button_controller.cc +++ b/ash/shelf/app_list_button_controller.cc
@@ -7,6 +7,7 @@ #include "ash/app_list/app_list_controller_impl.h" #include "ash/assistant/assistant_controller.h" #include "ash/home_screen/home_screen_controller.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/session/session_controller_impl.h" #include "ash/shelf/app_list_button.h" #include "ash/shelf/assistant_overlay.h" @@ -14,7 +15,6 @@ #include "ash/shelf/shelf_view.h" #include "ash/shell.h" #include "ash/shell_state.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "base/bind.h" #include "base/logging.h" @@ -53,7 +53,7 @@ shell->app_list_controller()->AddObserver(this); shell->session_controller()->AddObserver(this); shell->tablet_mode_controller()->AddObserver(this); - shell->voice_interaction_controller()->AddLocalObserver(this); + VoiceInteractionController::Get()->AddLocalObserver(this); // Initialize voice interaction overlay and sync the flags if active user // session has already started. This could happen when an external monitor @@ -74,7 +74,7 @@ if (shell->tablet_mode_controller()) shell->tablet_mode_controller()->RemoveObserver(this); shell->session_controller()->RemoveObserver(this); - shell->voice_interaction_controller()->RemoveLocalObserver(this); + VoiceInteractionController::Get()->RemoveLocalObserver(this); } bool AppListButtonController::MaybeHandleGestureEvent(ui::GestureEvent* event, @@ -146,8 +146,7 @@ } bool AppListButtonController::IsVoiceInteractionAvailable() { - VoiceInteractionController* controller = - Shell::Get()->voice_interaction_controller(); + VoiceInteractionController* controller = VoiceInteractionController::Get(); bool settings_enabled = controller->settings_enabled().value_or(false); bool consent_given = controller->consent_status() == mojom::ConsentStatus::kActivityControlAccepted; @@ -159,10 +158,8 @@ } bool AppListButtonController::IsVoiceInteractionRunning() { - return Shell::Get() - ->voice_interaction_controller() - ->voice_interaction_state() - .value_or(mojom::VoiceInteractionState::STOPPED) == + return VoiceInteractionController::Get()->voice_interaction_state().value_or( + mojom::VoiceInteractionState::STOPPED) == mojom::VoiceInteractionState::RUNNING; }
diff --git a/ash/shelf/app_list_button_unittest.cc b/ash/shelf/app_list_button_unittest.cc index c5cb16be..1ecad65 100644 --- a/ash/shelf/app_list_button_unittest.cc +++ b/ash/shelf/app_list_button_unittest.cc
@@ -16,6 +16,7 @@ #include "ash/kiosk_next/kiosk_next_shell_test_util.h" #include "ash/kiosk_next/mock_kiosk_next_shell_client.h" #include "ash/public/cpp/ash_features.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/root_window_controller.h" #include "ash/session/session_controller_impl.h" #include "ash/shelf/shelf.h" @@ -24,7 +25,6 @@ #include "ash/shelf/shelf_view_test_api.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "ash/wm/overview/overview_controller.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "base/command_line.h" @@ -192,10 +192,10 @@ CreateUserSessions(2); // Enable voice interaction in system settings. - Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true); - Shell::Get()->voice_interaction_controller()->NotifyFeatureAllowed( + VoiceInteractionController::Get()->NotifySettingsEnabled(true); + VoiceInteractionController::Get()->NotifyFeatureAllowed( mojom::AssistantAllowedState::ALLOWED); - Shell::Get()->voice_interaction_controller()->NotifyStatusChanged( + VoiceInteractionController::Get()->NotifyStatusChanged( mojom::VoiceInteractionState::STOPPED); ui::GestureEvent long_press = @@ -222,11 +222,11 @@ TEST_F(VoiceInteractionAppListButtonTest, LongPressGestureWithSecondaryUser) { // Disallowed by secondary user. - Shell::Get()->voice_interaction_controller()->NotifyFeatureAllowed( + VoiceInteractionController::Get()->NotifyFeatureAllowed( mojom::AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER); // Enable voice interaction in system settings. - Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true); + VoiceInteractionController::Get()->NotifySettingsEnabled(true); ui::GestureEvent long_press = CreateGestureEvent(ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS)); @@ -254,8 +254,8 @@ // Simulate a user who has already completed setup flow, but disabled voice // interaction in settings. - Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(false); - Shell::Get()->voice_interaction_controller()->NotifyFeatureAllowed( + VoiceInteractionController::Get()->NotifySettingsEnabled(false); + VoiceInteractionController::Get()->NotifyFeatureAllowed( mojom::AssistantAllowedState::ALLOWED); ui::GestureEvent long_press =
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc index b194444..4612824 100644 --- a/ash/shelf/shelf_widget.cc +++ b/ash/shelf/shelf_widget.cc
@@ -368,8 +368,6 @@ shelf_layout_manager_->GetShelfBackgroundType(), AnimationChangeType::IMMEDIATE); - views::Widget::AddObserver(this); - background_animator_.AddObserver(delegate_view_); shelf_->AddObserver(this); @@ -414,7 +412,6 @@ // Don't need to observe focus/activation during shutdown. Shell::Get()->focus_cycler()->RemoveWidget(this); SetFocusCycler(nullptr); - RemoveObserver(this); } void ShelfWidget::CreateStatusAreaWidget(aura::Window* status_container) { @@ -522,20 +519,22 @@ to_focus->GetFocusManager()->SetFocusedView(to_focus); } -void ShelfWidget::OnWidgetActivationChanged(views::Widget* widget, - bool active) { +bool ShelfWidget::OnNativeWidgetActivationChanged(bool active) { + if (!Widget::OnNativeWidgetActivationChanged(active)) + return false; if (active) { // Do not focus the default element if the widget activation came from the // another widget's focus cycling. The setter of // |activated_from_other_widget_| should handle focusing the correct view. if (activated_from_other_widget_) { activated_from_other_widget_ = false; - return; + return true; } delegate_view_->SetPaneFocusAndFocusDefault(); } else { delegate_view_->GetFocusManager()->ClearFocus(); } + return true; } void ShelfWidget::WillDeleteShelfLayoutManager() {
diff --git a/ash/shelf/shelf_widget.h b/ash/shelf/shelf_widget.h index 4f804e1..35c993c12 100644 --- a/ash/shelf/shelf_widget.h +++ b/ash/shelf/shelf_widget.h
@@ -16,7 +16,6 @@ #include "ash/shelf/shelf_observer.h" #include "base/macros.h" #include "ui/views/widget/widget.h" -#include "ui/views/widget/widget_observer.h" namespace app_list { class ApplicationDragAndDropHost; @@ -37,7 +36,6 @@ // the status area widget. There is one ShelfWidget per display. It is created // early during RootWindowController initialization. class ASH_EXPORT ShelfWidget : public views::Widget, - public views::WidgetObserver, public ShelfLayoutManagerObserver, public ShelfObserver, public SessionObserver, @@ -106,10 +104,12 @@ // and focuses it. void FocusFirstOrLastFocusableChild(bool last); - // Overridden from views::WidgetObserver: - void OnWidgetActivationChanged(views::Widget* widget, bool active) override; + // views::Widget: + void OnMouseEvent(ui::MouseEvent* event) override; + void OnGestureEvent(ui::GestureEvent* event) override; + bool OnNativeWidgetActivationChanged(bool active) override; - // ShelfLayoutManagerObserver overrides: + // ShelfLayoutManagerObserver: void WillDeleteShelfLayoutManager() override; // ShelfObserver: @@ -148,10 +148,6 @@ // Shows shelf widget if IsVisible() returns false. void ShowIfHidden(); - // views::Widget: - void OnMouseEvent(ui::MouseEvent* event) override; - void OnGestureEvent(ui::GestureEvent* event) override; - Shelf* shelf_; ShelfBackgroundAnimator background_animator_;
diff --git a/ash/shell.cc b/ash/shell.cc index eccb4cf..843e429 100644 --- a/ash/shell.cc +++ b/ash/shell.cc
@@ -72,6 +72,7 @@ #include "ash/public/cpp/ash_switches.h" #include "ash/public/cpp/shelf_model.h" #include "ash/public/cpp/shell_window_ids.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/root_window_controller.h" #include "ash/screenshot_delegate.h" #include "ash/session/session_controller_impl.h" @@ -119,7 +120,6 @@ #include "ash/touch/touch_devices_controller.h" #include "ash/tray_action/tray_action.h" #include "ash/utility/screenshot_controller.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "ash/wallpaper/wallpaper_controller_impl.h" #include "ash/wayland/wayland_server_controller.h" #include "ash/wm/ash_focus_rules.h"
diff --git a/ash/shell.h b/ash/shell.h index be213d4b..d8c2019 100644 --- a/ash/shell.h +++ b/ash/shell.h
@@ -492,9 +492,6 @@ } UserMetricsRecorder* metrics() { return user_metrics_recorder_.get(); } VideoDetector* video_detector() { return video_detector_.get(); } - VoiceInteractionController* voice_interaction_controller() { - return voice_interaction_controller_.get(); - } VpnList* vpn_list() { return vpn_list_.get(); } WallpaperControllerImpl* wallpaper_controller() { return wallpaper_controller_.get();
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller.cc b/ash/system/accessibility/autoclick_menu_bubble_controller.cc index 7759591c..c6cb458bc 100644 --- a/ash/system/accessibility/autoclick_menu_bubble_controller.cc +++ b/ash/system/accessibility/autoclick_menu_bubble_controller.cc
@@ -15,6 +15,8 @@ #include "ash/wm/work_area_insets.h" #include "ash/wm/workspace/workspace_layout_manager.h" #include "ash/wm/workspace_controller.h" +#include "base/command_line.h" +#include "ui/accessibility/accessibility_switches.h" #include "ui/aura/window_tree_host.h" #include "ui/compositor/layer.h" #include "ui/compositor/scoped_layer_animation_settings.h" @@ -25,6 +27,7 @@ namespace { // Autoclick menu constants. const int kAutoclickMenuWidth = 321; +const int kAutoclickMenuWidthWithScroll = 371; const int kAutoclickMenuHeight = 64; } // namespace @@ -72,27 +75,30 @@ aura::Window* window = Shell::GetPrimaryRootWindow(); gfx::Rect work_area = WorkAreaInsets::ForWindow(window)->user_work_area_bounds(); + int width = base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalAccessibilityAutoclick) + ? kAutoclickMenuWidthWithScroll + : kAutoclickMenuWidth; gfx::Rect new_bounds; switch (new_position) { case mojom::AutoclickMenuPosition::kBottomRight: - new_bounds = gfx::Rect(work_area.right() - kAutoclickMenuWidth, - work_area.bottom() - kAutoclickMenuHeight, - kAutoclickMenuWidth, kAutoclickMenuHeight); + new_bounds = gfx::Rect(work_area.right() - width, + work_area.bottom() - kAutoclickMenuHeight, width, + kAutoclickMenuHeight); break; case mojom::AutoclickMenuPosition::kBottomLeft: new_bounds = gfx::Rect(work_area.x(), work_area.bottom() - kAutoclickMenuHeight, - kAutoclickMenuWidth, kAutoclickMenuHeight); + width, kAutoclickMenuHeight); break; case mojom::AutoclickMenuPosition::kTopLeft: // Setting the top to 1 instead of 0 so that the view is drawn on screen. - new_bounds = gfx::Rect(work_area.x(), 1, kAutoclickMenuWidth, - kAutoclickMenuHeight); + new_bounds = gfx::Rect(work_area.x(), 1, width, kAutoclickMenuHeight); break; case mojom::AutoclickMenuPosition::kTopRight: // Setting the top to 1 instead of 0 so that the view is drawn on screen. - new_bounds = gfx::Rect(work_area.right() - kAutoclickMenuWidth, 1, - kAutoclickMenuWidth, kAutoclickMenuHeight); + new_bounds = + gfx::Rect(work_area.right() - width, 1, width, kAutoclickMenuHeight); break; case mojom::AutoclickMenuPosition::kSystemDefault: return; @@ -135,8 +141,12 @@ Shell::GetPrimaryRootWindow(), kShellWindowId_AutoclickContainer); init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect; init_params.insets = gfx::Insets(kCollisionWindowWorkAreaInsetsDp); - init_params.min_width = kAutoclickMenuWidth; - init_params.max_width = kAutoclickMenuWidth; + int width = base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalAccessibilityAutoclick) + ? kAutoclickMenuWidthWithScroll + : kAutoclickMenuWidth; + init_params.min_width = width; + init_params.max_width = width; init_params.corner_radius = kUnifiedTrayCornerRadius; init_params.has_shadow = false; bubble_view_ = new AutoclickMenuBubbleView(init_params);
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller.h b/ash/system/accessibility/autoclick_menu_bubble_controller.h index 0a21190..9c99179 100644 --- a/ash/system/accessibility/autoclick_menu_bubble_controller.h +++ b/ash/system/accessibility/autoclick_menu_bubble_controller.h
@@ -33,11 +33,11 @@ // Shows or hides the bubble. void SetBubbleVisibility(bool is_visible); - // Performs the mouse events on the bubble. at the given location in DIPs. + // Performs a mouse event on the bubble at the given location in DIPs. void ClickOnBubble(gfx::Point location_in_dips, int mouse_event_flags); - // Whether the tray button or the bubble, if the bubble exists, contain - // the given screen point. + // Whether the the bubble, if the bubble exists, contains the given screen + // point. bool ContainsPointInScreen(const gfx::Point& point); // TrayBubbleView::Delegate:
diff --git a/ash/system/accessibility/autoclick_menu_view.cc b/ash/system/accessibility/autoclick_menu_view.cc index 7ba111e2..fad944d 100644 --- a/ash/system/accessibility/autoclick_menu_view.cc +++ b/ash/system/accessibility/autoclick_menu_view.cc
@@ -10,8 +10,10 @@ #include "ash/strings/grit/ash_strings.h" #include "ash/system/tray/tray_constants.h" #include "ash/system/unified/top_shortcut_button.h" +#include "base/command_line.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/user_metrics.h" +#include "ui/accessibility/accessibility_switches.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/canvas.h" #include "ui/gfx/paint_vector_icon.h" @@ -175,6 +177,13 @@ kAutoclickPositionBottomLeftIcon, IDS_ASH_AUTOCLICK_OPTION_CHANGE_POSITION, kPanelPositionButtonSize)) { + // TODO(katie): Initialize scroll above once it launches, target in M77. + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalAccessibilityAutoclick)) { + scroll_button_ = new AutoclickMenuButton(this, kAutoclickScrollIcon, + IDS_ASH_AUTOCLICK_OPTION_SCROLL); + } + // Set view IDs for testing. left_click_button_->SetId(ButtonId::kLeftClick); right_click_button_->SetId(ButtonId::kRightClick); @@ -182,6 +191,8 @@ drag_button_->SetId(ButtonId::kDragAndDrop); pause_button_->SetId(ButtonId::kPause); position_button_->SetId(ButtonId::kPosition); + if (scroll_button_) + scroll_button_->SetId(ButtonId::kScroll); std::unique_ptr<views::BoxLayout> layout = std::make_unique<views::BoxLayout>( views::BoxLayout::kHorizontal, gfx::Insets(), 0); @@ -197,6 +208,8 @@ action_button_container->AddChildView(right_click_button_); action_button_container->AddChildView(double_click_button_); action_button_container->AddChildView(drag_button_); + if (scroll_button_) + action_button_container->AddChildView(scroll_button_); action_button_container->AddChildView(pause_button_); AddChildView(action_button_container); @@ -230,6 +243,8 @@ double_click_button_->SetToggled(type == mojom::AutoclickEventType::kDoubleClick); drag_button_->SetToggled(type == mojom::AutoclickEventType::kDragAndDrop); + if (scroll_button_) + scroll_button_->SetToggled(type == mojom::AutoclickEventType::kScroll); pause_button_->SetToggled(type == mojom::AutoclickEventType::kNoAction); if (type != mojom::AutoclickEventType::kNoAction) event_type_ = type; @@ -297,6 +312,8 @@ type = mojom::AutoclickEventType::kDoubleClick; } else if (sender == drag_button_) { type = mojom::AutoclickEventType::kDragAndDrop; + } else if (sender == scroll_button_) { + type = mojom::AutoclickEventType::kScroll; } else if (sender == pause_button_) { // If the pause button was already selected, tapping it again turns off // pause and returns to the previous type.
diff --git a/ash/system/accessibility/autoclick_menu_view.h b/ash/system/accessibility/autoclick_menu_view.h index 51384ce..3cd5ac9 100644 --- a/ash/system/accessibility/autoclick_menu_view.h +++ b/ash/system/accessibility/autoclick_menu_view.h
@@ -41,7 +41,8 @@ kRightClick = 3, kDoubleClick = 4, kDragAndDrop = 5, - kPause = 6 + kScroll = 6, + kPause = 7, }; AutoclickMenuView(mojom::AutoclickEventType type, @@ -63,6 +64,7 @@ AutoclickMenuButton* right_click_button_; AutoclickMenuButton* double_click_button_; AutoclickMenuButton* drag_button_; + AutoclickMenuButton* scroll_button_ = nullptr; AutoclickMenuButton* pause_button_; AutoclickMenuButton* position_button_;
diff --git a/ash/system/palette/palette_tray_unittest.cc b/ash/system/palette/palette_tray_unittest.cc index 7949626d..a98387f 100644 --- a/ash/system/palette/palette_tray_unittest.cc +++ b/ash/system/palette/palette_tray_unittest.cc
@@ -17,6 +17,7 @@ #include "ash/public/cpp/ash_pref_names.h" #include "ash/public/cpp/ash_switches.h" #include "ash/public/cpp/stylus_utils.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/public/interfaces/voice_interaction_controller.mojom.h" #include "ash/root_window_controller.h" #include "ash/session/session_controller_impl.h" @@ -30,7 +31,6 @@ #include "ash/test/ash_test_base.h" #include "ash/test/ash_test_helper.h" #include "ash/test_shell_delegate.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "base/command_line.h" #include "base/memory/ptr_util.h" #include "base/run_loop.h" @@ -345,11 +345,11 @@ TEST_F(PaletteTrayTestWithVoiceInteraction, MetalayerToolActivatesHighlighter) { ui::ScopedAnimationDurationScaleMode animation_duration_mode( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); - Shell::Get()->voice_interaction_controller()->NotifyStatusChanged( + VoiceInteractionController::Get()->NotifyStatusChanged( mojom::VoiceInteractionState::RUNNING); - Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true); - Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(true); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->NotifySettingsEnabled(true); + VoiceInteractionController::Get()->NotifyContextEnabled(true); + VoiceInteractionController::Get()->FlushForTesting(); ui::test::EventGenerator* generator = GetEventGenerator(); generator->EnterPenPointerMode(); @@ -411,8 +411,8 @@ // Disabling metalayer support in the delegate should disable the palette // tool. test_api_->palette_tool_manager()->ActivateTool(PaletteToolId::METALAYER); - Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(false); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->NotifyContextEnabled(false); + VoiceInteractionController::Get()->FlushForTesting(); EXPECT_FALSE(metalayer_enabled()); // With the metalayer disabled again, press/drag does not activate the @@ -426,11 +426,11 @@ StylusBarrelButtonActivatesHighlighter) { ui::ScopedAnimationDurationScaleMode animation_duration_mode( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); - Shell::Get()->voice_interaction_controller()->NotifyStatusChanged( + VoiceInteractionController::Get()->NotifyStatusChanged( mojom::VoiceInteractionState::NOT_READY); - Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(false); - Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(false); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->NotifySettingsEnabled(false); + VoiceInteractionController::Get()->NotifyContextEnabled(false); + VoiceInteractionController::Get()->FlushForTesting(); ui::test::EventGenerator* generator = GetEventGenerator(); generator->EnterPenPointerMode(); @@ -450,23 +450,23 @@ false /* no highlighter on press */); // Enable one of the two user prefs, should not be sufficient. - Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(true); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->NotifyContextEnabled(true); + VoiceInteractionController::Get()->FlushForTesting(); WaitDragAndAssertMetalayer("one pref enabled", origin, ui::EF_LEFT_MOUSE_BUTTON, false /* no metalayer */, false /* no highlighter on press */); // Enable the other user pref, still not sufficient. - Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->NotifySettingsEnabled(true); + VoiceInteractionController::Get()->FlushForTesting(); WaitDragAndAssertMetalayer("two prefs enabled", origin, ui::EF_LEFT_MOUSE_BUTTON, false /* no metalayer */, false /* no highlighter on press */); // Once the service is ready, the button should start working. - Shell::Get()->voice_interaction_controller()->NotifyStatusChanged( + VoiceInteractionController::Get()->NotifyStatusChanged( mojom::VoiceInteractionState::RUNNING); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->FlushForTesting(); // Press and drag with no button, still no highlighter. WaitDragAndAssertMetalayer("all enabled, no button ", origin, ui::EF_NONE, @@ -530,8 +530,8 @@ // Disable the metalayer support. // This should deactivate both the palette tool and the highlighter. - Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(false); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->NotifyContextEnabled(false); + VoiceInteractionController::Get()->FlushForTesting(); EXPECT_FALSE(test_api_->palette_tool_manager()->IsToolActive( PaletteToolId::METALAYER));
diff --git a/ash/system/palette/tools/metalayer_mode.cc b/ash/system/palette/tools/metalayer_mode.cc index 64979b0..c76e7d6 100644 --- a/ash/system/palette/tools/metalayer_mode.cc +++ b/ash/system/palette/tools/metalayer_mode.cc
@@ -4,6 +4,7 @@ #include "ash/system/palette/tools/metalayer_mode.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/resources/vector_icons/vector_icons.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" @@ -13,7 +14,6 @@ #include "ash/system/toast/toast_manager.h" #include "ash/system/tray/hover_highlight_view.h" #include "ash/system/tray/tray_constants.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "base/bind.h" #include "ui/base/l10n/l10n_util.h" #include "ui/events/event.h" @@ -39,13 +39,13 @@ : CommonPaletteTool(delegate), weak_factory_(this) { Shell::Get()->AddPreTargetHandler(this); - Shell::Get()->voice_interaction_controller()->AddLocalObserver(this); + VoiceInteractionController::Get()->AddLocalObserver(this); Shell::Get()->highlighter_controller()->AddObserver(this); } MetalayerMode::~MetalayerMode() { Shell::Get()->highlighter_controller()->RemoveObserver(this); - Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this); + VoiceInteractionController::Get()->RemoveLocalObserver(this); Shell::Get()->RemovePreTargetHandler(this); }
diff --git a/ash/system/palette/tools/metalayer_unittest.cc b/ash/system/palette/tools/metalayer_unittest.cc index abc72d0..f55e501 100644 --- a/ash/system/palette/tools/metalayer_unittest.cc +++ b/ash/system/palette/tools/metalayer_unittest.cc
@@ -6,6 +6,7 @@ #include "ash/highlighter/highlighter_controller.h" #include "ash/highlighter/highlighter_controller_test_api.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "ash/public/interfaces/voice_interaction_controller.mojom.h" #include "ash/shell.h" #include "ash/system/palette/mock_palette_tool_delegate.h" @@ -14,7 +15,6 @@ #include "ash/system/palette/tools/metalayer_mode.h" #include "ash/system/tray/hover_highlight_view.h" #include "ash/test/ash_test_base.h" -#include "ash/voice_interaction/voice_interaction_controller.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" @@ -88,15 +88,12 @@ const bool ready = state != mojom::VoiceInteractionState::NOT_READY; const bool selectable = allowed && enabled && context && ready; - Shell::Get()->voice_interaction_controller()->NotifyStatusChanged( - state); - Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled( - enabled); - Shell::Get()->voice_interaction_controller()->NotifyContextEnabled( - context); - Shell::Get()->voice_interaction_controller()->NotifyFeatureAllowed( + VoiceInteractionController::Get()->NotifyStatusChanged(state); + VoiceInteractionController::Get()->NotifySettingsEnabled(enabled); + VoiceInteractionController::Get()->NotifyContextEnabled(context); + VoiceInteractionController::Get()->NotifyFeatureAllowed( allowed_state); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->FlushForTesting(); std::unique_ptr<views::View> view = base::WrapUnique(tool_->CreateView()); @@ -142,30 +139,30 @@ // Verifies that disabling the metalayer support disables the tool. TEST_F(MetalayerToolTest, MetalayerUnsupportedDisablesPaletteTool) { - Shell::Get()->voice_interaction_controller()->NotifyStatusChanged( + VoiceInteractionController::Get()->NotifyStatusChanged( mojom::VoiceInteractionState::RUNNING); - Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true); - Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(true); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->NotifySettingsEnabled(true); + VoiceInteractionController::Get()->NotifyContextEnabled(true); + VoiceInteractionController::Get()->FlushForTesting(); // Disabling the user prefs individually should disable the tool. tool_->OnEnable(); EXPECT_CALL(*palette_tool_delegate_.get(), DisableTool(PaletteToolId::METALAYER)); - Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(false); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->NotifySettingsEnabled(false); + VoiceInteractionController::Get()->FlushForTesting(); testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get()); - Shell::Get()->voice_interaction_controller()->NotifySettingsEnabled(true); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->NotifySettingsEnabled(true); + VoiceInteractionController::Get()->FlushForTesting(); tool_->OnEnable(); EXPECT_CALL(*palette_tool_delegate_.get(), DisableTool(PaletteToolId::METALAYER)); - Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(false); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->NotifyContextEnabled(false); + VoiceInteractionController::Get()->FlushForTesting(); testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get()); - Shell::Get()->voice_interaction_controller()->NotifyContextEnabled(true); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->NotifyContextEnabled(true); + VoiceInteractionController::Get()->FlushForTesting(); // Test VoiceInteractionState changes. tool_->OnEnable(); @@ -175,19 +172,19 @@ EXPECT_CALL(*palette_tool_delegate_.get(), DisableTool(PaletteToolId::METALAYER)) .Times(0); - Shell::Get()->voice_interaction_controller()->NotifyStatusChanged( + VoiceInteractionController::Get()->NotifyStatusChanged( mojom::VoiceInteractionState::STOPPED); - Shell::Get()->voice_interaction_controller()->NotifyStatusChanged( + VoiceInteractionController::Get()->NotifyStatusChanged( mojom::VoiceInteractionState::RUNNING); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->FlushForTesting(); testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get()); // Changing the state to NOT_READY should disable the tool. EXPECT_CALL(*palette_tool_delegate_.get(), DisableTool(PaletteToolId::METALAYER)); - Shell::Get()->voice_interaction_controller()->NotifyStatusChanged( + VoiceInteractionController::Get()->NotifyStatusChanged( mojom::VoiceInteractionState::NOT_READY); - Shell::Get()->voice_interaction_controller()->FlushForTesting(); + VoiceInteractionController::Get()->FlushForTesting(); testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get()); }
diff --git a/ash/voice_interaction/OWNERS b/ash/voice_interaction/OWNERS deleted file mode 100644 index bb65116..0000000 --- a/ash/voice_interaction/OWNERS +++ /dev/null
@@ -1,2 +0,0 @@ -per-file *_struct_traits*.*=set noparent -per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/ash/voice_interaction/voice_interaction_controller.cc b/ash/voice_interaction/voice_interaction_controller.cc deleted file mode 100644 index 01bca6f..0000000 --- a/ash/voice_interaction/voice_interaction_controller.cc +++ /dev/null
@@ -1,204 +0,0 @@ -// 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 "ash/voice_interaction/voice_interaction_controller.h" - -#include <utility> - -#include "chromeos/constants/chromeos_switches.h" - -namespace ash { - -VoiceInteractionController::VoiceInteractionController() { - if (chromeos::switches::IsAssistantEnabled()) - voice_interaction_state_ = mojom::VoiceInteractionState::NOT_READY; -} - -VoiceInteractionController::~VoiceInteractionController() = default; - -void VoiceInteractionController::BindRequest( - mojom::VoiceInteractionControllerRequest request) { - bindings_.AddBinding(this, std::move(request)); -} - -void VoiceInteractionController::NotifyStatusChanged( - mojom::VoiceInteractionState state) { - if (voice_interaction_state_ == state) - return; - - voice_interaction_state_ = state; - observers_.ForAllPtrs([state](auto* observer) { - observer->OnVoiceInteractionStatusChanged(state); - }); - for (auto& observer : local_observers_) - observer.OnVoiceInteractionStatusChanged(state); -} - -void VoiceInteractionController::NotifySettingsEnabled(bool enabled) { - if (settings_enabled_.has_value() && settings_enabled_.value() == enabled) - return; - - settings_enabled_ = enabled; - observers_.ForAllPtrs([enabled](auto* observer) { - observer->OnVoiceInteractionSettingsEnabled(enabled); - }); - for (auto& observer : local_observers_) - observer.OnVoiceInteractionSettingsEnabled(enabled); -} - -void VoiceInteractionController::NotifyContextEnabled(bool enabled) { - if (context_enabled_.has_value() && context_enabled_.value() == enabled) - return; - - context_enabled_ = enabled; - observers_.ForAllPtrs([enabled](auto* observer) { - observer->OnVoiceInteractionContextEnabled(enabled); - }); - for (auto& observer : local_observers_) - observer.OnVoiceInteractionContextEnabled(enabled); -} - -void VoiceInteractionController::NotifyHotwordEnabled(bool enabled) { - if (hotword_enabled_.has_value() && hotword_enabled_.value() == enabled) - return; - - hotword_enabled_ = enabled; - observers_.ForAllPtrs([enabled](auto* observer) { - observer->OnVoiceInteractionHotwordEnabled(enabled); - }); - for (auto& observer : local_observers_) - observer.OnVoiceInteractionHotwordEnabled(enabled); -} - -void VoiceInteractionController::NotifyHotwordAlwaysOn(bool always_on) { - if (hotword_always_on_.has_value() && hotword_always_on_.value() == always_on) - return; - - hotword_always_on_ = always_on; - observers_.ForAllPtrs([always_on](auto* observer) { - observer->OnVoiceInteractionHotwordAlwaysOn(always_on); - }); - for (auto& observer : local_observers_) - observer.OnVoiceInteractionHotwordAlwaysOn(always_on); -} - -void VoiceInteractionController::NotifyConsentStatus( - mojom::ConsentStatus consent_status) { - if (consent_status_.has_value() && consent_status_.value() == consent_status) - return; - - consent_status_ = consent_status; - observers_.ForAllPtrs([consent_status](auto* observer) { - observer->OnVoiceInteractionConsentStatusUpdated(consent_status); - }); - for (auto& observer : local_observers_) - observer.OnVoiceInteractionConsentStatusUpdated(consent_status); -} - -void VoiceInteractionController::NotifyFeatureAllowed( - mojom::AssistantAllowedState state) { - if (allowed_state_ == state) - return; - - allowed_state_ = state; - observers_.ForAllPtrs([state](auto* observer) { - observer->OnAssistantFeatureAllowedChanged(state); - }); - for (auto& observer : local_observers_) - observer.OnAssistantFeatureAllowedChanged(state); -} - -void VoiceInteractionController::NotifyNotificationEnabled(bool enabled) { - notification_enabled_ = enabled; -} - -void VoiceInteractionController::NotifyLocaleChanged( - const std::string& locale) { - if (locale_ == locale) - return; - - locale_ = locale; - observers_.ForAllPtrs( - [locale](auto* observer) { observer->OnLocaleChanged(locale); }); - for (auto& observer : local_observers_) - observer.OnLocaleChanged(locale); -} - -void VoiceInteractionController::NotifyLaunchWithMicOpen( - bool launch_with_mic_open) { - launch_with_mic_open_ = launch_with_mic_open; -} - -void VoiceInteractionController::NotifyArcPlayStoreEnabledChanged( - bool enabled) { - if (arc_play_store_enabled_ == enabled) - return; - - arc_play_store_enabled_ = enabled; - - observers_.ForAllPtrs([enabled](auto* observer) { - observer->OnArcPlayStoreEnabledChanged(enabled); - }); - for (auto& observer : local_observers_) - observer.OnArcPlayStoreEnabledChanged(enabled); -} - -void VoiceInteractionController::NotifyLockedFullScreenStateChanged( - bool enabled) { - if (locked_full_screen_enabled_ == enabled) - return; - - locked_full_screen_enabled_ = enabled; - - observers_.ForAllPtrs([enabled](auto* observer) { - observer->OnLockedFullScreenStateChanged(enabled); - }); - for (auto& observer : local_observers_) - observer.OnLockedFullScreenStateChanged(enabled); -} - -void VoiceInteractionController::AddObserver( - mojom::VoiceInteractionObserverPtr observer) { - InitObserver(observer.get()); - observers_.AddPtr(std::move(observer)); -} - -void VoiceInteractionController::AddLocalObserver( - DefaultVoiceInteractionObserver* observer) { - InitObserver(observer); - local_observers_.AddObserver(observer); -} - -void VoiceInteractionController::RemoveLocalObserver( - DefaultVoiceInteractionObserver* observer) { - local_observers_.RemoveObserver(observer); -} - -void VoiceInteractionController::InitObserver( - mojom::VoiceInteractionObserver* observer) { - if (voice_interaction_state_.has_value()) - observer->OnVoiceInteractionStatusChanged(voice_interaction_state_.value()); - if (settings_enabled_.has_value()) - observer->OnVoiceInteractionSettingsEnabled(settings_enabled_.value()); - if (context_enabled_.has_value()) - observer->OnVoiceInteractionContextEnabled(context_enabled_.value()); - if (hotword_enabled_.has_value()) - observer->OnVoiceInteractionHotwordEnabled(hotword_enabled_.value()); - if (consent_status_.has_value()) - observer->OnVoiceInteractionConsentStatusUpdated(consent_status_.value()); - if (hotword_always_on_.has_value()) - observer->OnVoiceInteractionHotwordAlwaysOn(hotword_always_on_.value()); - if (allowed_state_.has_value()) - observer->OnAssistantFeatureAllowedChanged(allowed_state_.value()); - if (locale_.has_value()) - observer->OnLocaleChanged(locale_.value()); - if (arc_play_store_enabled_.has_value()) - observer->OnArcPlayStoreEnabledChanged(arc_play_store_enabled_.value()); -} - -void VoiceInteractionController::FlushForTesting() { - observers_.FlushForTesting(); -} - -} // namespace ash
diff --git a/ash/voice_interaction/voice_interaction_controller.h b/ash/voice_interaction/voice_interaction_controller.h deleted file mode 100644 index f8dcaa12..0000000 --- a/ash/voice_interaction/voice_interaction_controller.h +++ /dev/null
@@ -1,73 +0,0 @@ -// 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 ASH_VOICE_INTERACTION_VOICE_INTERACTION_CONTROLLER_H_ -#define ASH_VOICE_INTERACTION_VOICE_INTERACTION_CONTROLLER_H_ - -#include <memory> -#include <string> - -#include "ash/ash_export.h" -#include "ash/public/cpp/assistant/assistant_state_base.h" -#include "ash/public/cpp/assistant/default_voice_interaction_observer.h" -#include "ash/public/interfaces/voice_interaction_controller.mojom.h" -#include "mojo/public/cpp/bindings/binding_set.h" -#include "mojo/public/cpp/bindings/interface_ptr_set.h" - -namespace ash { - -class ASH_EXPORT VoiceInteractionController - : public mojom::VoiceInteractionController, - public AssistantStateBase { - public: - VoiceInteractionController(); - ~VoiceInteractionController() override; - - void BindRequest(mojom::VoiceInteractionControllerRequest request); - - // ash::mojom::VoiceInteractionController: - void NotifyStatusChanged(mojom::VoiceInteractionState state) override; - void NotifySettingsEnabled(bool enabled) override; - void NotifyContextEnabled(bool enabled) override; - void NotifyHotwordEnabled(bool enabled) override; - void NotifyHotwordAlwaysOn(bool always_on) override; - void NotifyConsentStatus(mojom::ConsentStatus consent_status) override; - void NotifyFeatureAllowed(mojom::AssistantAllowedState state) override; - void NotifyNotificationEnabled(bool enabled) override; - void NotifyLocaleChanged(const std::string& locale) override; - void NotifyLaunchWithMicOpen(bool launch_with_mic_open) override; - void NotifyArcPlayStoreEnabledChanged(bool enabled) override; - void NotifyLockedFullScreenStateChanged(bool enabled) override; - void AddObserver(mojom::VoiceInteractionObserverPtr observer) override; - - // Adding local observers in the same process. - void AddLocalObserver(DefaultVoiceInteractionObserver* observer); - void RemoveLocalObserver(DefaultVoiceInteractionObserver* observer); - void InitObserver(mojom::VoiceInteractionObserver* observer); - - bool notification_enabled() const { return notification_enabled_; } - - bool launch_with_mic_open() const { return launch_with_mic_open_; } - - void FlushForTesting(); - - private: - // Whether notification is enabled. - bool notification_enabled_ = false; - - // Whether the Assistant should launch with mic open; - bool launch_with_mic_open_ = false; - - mojo::BindingSet<mojom::VoiceInteractionController> bindings_; - - mojo::InterfacePtrSet<mojom::VoiceInteractionObserver> observers_; - - base::ObserverList<DefaultVoiceInteractionObserver> local_observers_; - - DISALLOW_COPY_AND_ASSIGN(VoiceInteractionController); -}; - -} // namespace ash - -#endif // ASH_VOICE_INTERACTION_VOICE_INTERACTION_CONTROLLER_H_
diff --git a/ash/voice_interaction/voice_interaction_controller_unittest.cc b/ash/voice_interaction/voice_interaction_controller_unittest.cc deleted file mode 100644 index fb0e922e..0000000 --- a/ash/voice_interaction/voice_interaction_controller_unittest.cc +++ /dev/null
@@ -1,177 +0,0 @@ -// 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 "ash/voice_interaction/voice_interaction_controller.h" - -#include <memory> -#include <utility> - -#include "ash/public/interfaces/voice_interaction_controller.mojom.h" -#include "ash/shell.h" -#include "ash/test/ash_test_base.h" -#include "mojo/public/cpp/bindings/binding.h" - -namespace ash { -namespace { - -class TestVoiceInteractionObserver : public mojom::VoiceInteractionObserver { - public: - TestVoiceInteractionObserver() : voice_interaction_binding_(this) {} - - ~TestVoiceInteractionObserver() override = default; - - // mojom::VoiceInteractionObserver overrides: - void OnVoiceInteractionStatusChanged( - mojom::VoiceInteractionState state) override { - state_ = state; - } - void OnVoiceInteractionSettingsEnabled(bool enabled) override { - settings_enabled_ = enabled; - } - void OnVoiceInteractionContextEnabled(bool enabled) override { - context_enabled_ = enabled; - } - void OnVoiceInteractionHotwordAlwaysOn(bool always_on) override { - hotword_always_on_ = always_on; - } - void OnVoiceInteractionHotwordEnabled(bool enabled) override { - hotword_enabled_ = enabled; - } - void OnVoiceInteractionConsentStatusUpdated( - mojom::ConsentStatus consent_status) override { - consent_status_ = consent_status; - } - void OnAssistantFeatureAllowedChanged( - mojom::AssistantAllowedState state) override {} - void OnLocaleChanged(const std::string& locale) override {} - void OnArcPlayStoreEnabledChanged(bool enabled) override { - arc_play_store_enabled_ = enabled; - } - void OnLockedFullScreenStateChanged(bool enabled) override {} - - mojom::VoiceInteractionState voice_interaction_state() const { - return state_; - } - bool settings_enabled() const { return settings_enabled_; } - bool context_enabled() const { return context_enabled_; } - bool hotword_always_on() const { return hotword_always_on_; } - bool hotword_enabled() const { return hotword_enabled_; } - bool arc_play_store_enabled() const { return arc_play_store_enabled_; } - mojom::ConsentStatus consent_status() const { return consent_status_; } - - void SetVoiceInteractionController(VoiceInteractionController* controller) { - mojom::VoiceInteractionObserverPtr ptr; - voice_interaction_binding_.Bind(mojo::MakeRequest(&ptr)); - controller->AddObserver(std::move(ptr)); - } - - private: - mojom::VoiceInteractionState state_ = mojom::VoiceInteractionState::STOPPED; - bool settings_enabled_ = false; - bool context_enabled_ = false; - bool hotword_always_on_ = false; - bool hotword_enabled_ = false; - bool arc_play_store_enabled_ = false; - mojom::ConsentStatus consent_status_ = mojom::ConsentStatus::kUnknown; - - mojo::Binding<mojom::VoiceInteractionObserver> voice_interaction_binding_; - - DISALLOW_COPY_AND_ASSIGN(TestVoiceInteractionObserver); -}; - -class VoiceInteractionControllerTest : public AshTestBase { - public: - VoiceInteractionControllerTest() = default; - ~VoiceInteractionControllerTest() override = default; - - void SetUp() override { - AshTestBase::SetUp(); - - observer_ = std::make_unique<TestVoiceInteractionObserver>(); - observer_->SetVoiceInteractionController(controller()); - } - - void TearDown() override { - observer_.reset(); - - AshTestBase::TearDown(); - } - - protected: - VoiceInteractionController* controller() { - return Shell::Get()->voice_interaction_controller(); - } - - TestVoiceInteractionObserver* observer() { return observer_.get(); } - - private: - std::unique_ptr<TestVoiceInteractionObserver> observer_; - - DISALLOW_COPY_AND_ASSIGN(VoiceInteractionControllerTest); -}; - -} // namespace - -TEST_F(VoiceInteractionControllerTest, NotifyStatusChanged) { - controller()->NotifyStatusChanged(mojom::VoiceInteractionState::RUNNING); - controller()->FlushForTesting(); - - // The cached state should be updated. - EXPECT_EQ(mojom::VoiceInteractionState::RUNNING, - controller()->voice_interaction_state()); - // The observers should be notified. - EXPECT_EQ(mojom::VoiceInteractionState::RUNNING, - observer()->voice_interaction_state()); -} - -TEST_F(VoiceInteractionControllerTest, NotifySettingsEnabled) { - controller()->NotifySettingsEnabled(true); - controller()->FlushForTesting(); - // The cached state should be updated. - EXPECT_TRUE(controller()->settings_enabled()); - // The observers should be notified. - EXPECT_TRUE(observer()->settings_enabled()); -} - -TEST_F(VoiceInteractionControllerTest, NotifyContextEnabled) { - controller()->NotifyContextEnabled(true); - controller()->FlushForTesting(); - // The observers should be notified. - EXPECT_TRUE(observer()->context_enabled()); -} - -TEST_F(VoiceInteractionControllerTest, NotifyHotwordAlwaysOn) { - controller()->NotifyHotwordAlwaysOn(true); - controller()->FlushForTesting(); - // The observers should be notified. - EXPECT_TRUE(observer()->hotword_always_on()); -} - -TEST_F(VoiceInteractionControllerTest, NotifyHotwordEnabled) { - controller()->NotifyHotwordEnabled(true); - controller()->FlushForTesting(); - // The observers should be notified. - EXPECT_TRUE(observer()->hotword_enabled()); -} - -TEST_F(VoiceInteractionControllerTest, NotifyConsentStatus) { - controller()->NotifyConsentStatus( - mojom::ConsentStatus::kActivityControlAccepted); - controller()->FlushForTesting(); - // The cached state should be updated. - EXPECT_TRUE(controller()->consent_status() == - mojom::ConsentStatus::kActivityControlAccepted); - // The observers should be notified. - EXPECT_TRUE(observer()->consent_status() == - mojom::ConsentStatus::kActivityControlAccepted); -} - -TEST_F(VoiceInteractionControllerTest, NotifyArcPlayStoreEnabledChanged) { - controller()->NotifyArcPlayStoreEnabledChanged(true); - controller()->FlushForTesting(); - // The observers should be notified. - EXPECT_TRUE(observer()->arc_play_store_enabled()); -} - -} // namespace ash
diff --git a/ash/wm/fullscreen_window_finder.cc b/ash/wm/fullscreen_window_finder.cc index 18b6e0d1..d2c05756 100644 --- a/ash/wm/fullscreen_window_finder.cc +++ b/ash/wm/fullscreen_window_finder.cc
@@ -52,7 +52,7 @@ return nullptr; } -// Given a |topmost_window|, returns it or one of its tranisent parents if the +// Given a |topmost_window|, returns it or one of its transient parents if the // returned window is fullscreen or pinned. Otherwise, return nullptr. aura::Window* FindFullscreenOrPinnedWindow(aura::Window* topmost_window) { while (topmost_window) {
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc index eba82c0..2d037f9b 100644 --- a/ash/wm/overview/overview_controller.cc +++ b/ash/wm/overview/overview_controller.cc
@@ -302,7 +302,7 @@ base::EraseIf(windows, wm::ShouldExcludeForOverview); // Overview windows will handle showing their transient related windows, so if // a window in |windows| has a transient root also in |windows|, we can remove - // it as the tranisent root will handle showing the window. + // it as the transient root will handle showing the window. wm::RemoveTransientDescendants(&windows); // We may want to slide the overview grid in or out in some cases, even if @@ -507,7 +507,7 @@ // Show a toast if the window cannot be snapped. if (!CanSnapInSplitview(active_window)) { - split_view_controller->ShowAppCannotSnapToast(); + ShowAppCannotSnapToast(); return; }
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc index d7cfc1b..980ddd6 100644 --- a/ash/wm/overview/overview_session.cc +++ b/ash/wm/overview/overview_session.cc
@@ -820,8 +820,11 @@ // TODO(crbug.com/952315): Explore better ways to handle this splitview + // overview + applist case. Shell* shell = Shell::Get(); - if (shell->app_list_controller() && shell->app_list_controller()->IsVisible()) + if (shell->app_list_controller() && + shell->app_list_controller()->IsVisible() && + Shell::Get()->split_view_controller()->InClamshellSplitViewMode()) { return; + } if (event->type() != ui::ET_KEY_PRESSED) return;
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc index 338c7b3..24ae9ef 100644 --- a/ash/wm/overview/overview_session_unittest.cc +++ b/ash/wm/overview/overview_session_unittest.cc
@@ -4402,6 +4402,27 @@ EXPECT_FALSE(InOverviewSession()); } +// Test that in tablet mode, pressing tab key in overview should not crash. +TEST_F(SplitViewOverviewSessionTest, NoCrashWhenPressTabKey) { + std::unique_ptr<aura::Window> window(CreateWindow(gfx::Rect(400, 400))); + std::unique_ptr<aura::Window> window2(CreateWindow(gfx::Rect(400, 400))); + + // In overview, there should be no crash when pressing tab key. + ToggleOverview(); + EXPECT_TRUE(InOverviewSession()); + SendKey(ui::VKEY_TAB); + EXPECT_TRUE(InOverviewSession()); + + // When splitview and overview are both active, there should be no crash when + // pressing tab key. + split_view_controller()->SnapWindow(window.get(), SplitViewController::LEFT); + EXPECT_TRUE(InOverviewSession()); + EXPECT_TRUE(split_view_controller()->InSplitViewMode()); + + SendKey(ui::VKEY_TAB); + EXPECT_TRUE(InOverviewSession()); +} + // Test the split view and overview functionalities in clamshell mode. Split // view is only active when overview is active in clamshell mode. class SplitViewOverviewSessionInClamshellTest
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc index 6aa4928..303f7c9c 100644 --- a/ash/wm/overview/overview_window_drag_controller.cc +++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -246,7 +246,7 @@ } else { split_view_controller_->EndSplitView(); overview_session_->SelectWindow(item_); - split_view_controller_->ShowAppCannotSnapToast(); + ShowAppCannotSnapToast(); } current_drag_behavior_ = DragBehavior::kNoDrag; UnpauseOcclusionTracker();
diff --git a/ash/wm/overview/scoped_overview_transform_window.cc b/ash/wm/overview/scoped_overview_transform_window.cc index 3c0157aa..b7f13e1 100644 --- a/ash/wm/overview/scoped_overview_transform_window.cc +++ b/ash/wm/overview/scoped_overview_transform_window.cc
@@ -27,6 +27,7 @@ #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "ui/aura/client/aura_constants.h" +#include "ui/aura/client/transient_window_client.h" #include "ui/aura/window.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_observer.h" @@ -177,37 +178,39 @@ original_mask_layer_(window_->layer()->layer_mask_layer()), weak_ptr_factory_(this) { type_ = GetWindowDimensionsType(window); - original_event_targeting_policy_ = window_->event_targeting_policy(); - window_->SetEventTargetingPolicy(aura::EventTargetingPolicy::kNone); - window_->SetProperty(kIsShowingInOverviewKey, true); - // Hide transient children which have been specified to be hidden in overview - // mode. std::vector<aura::Window*> transient_children_to_hide; for (auto* transient : wm::GetTransientTreeIterator(window)) { - if (transient == window) - continue; - - if (transient->GetProperty(kHideInOverviewKey)) - transient_children_to_hide.push_back(transient); - + targeting_policy_map_[transient] = transient->event_targeting_policy(); + transient->SetEventTargetingPolicy(aura::EventTargetingPolicy::kNone); transient->SetProperty(kIsShowingInOverviewKey, true); + + // Hide transient children which have been specified to be hidden in + // overview mode. + if (transient != window && transient->GetProperty(kHideInOverviewKey)) + transient_children_to_hide.push_back(transient); } if (!transient_children_to_hide.empty()) { hidden_transient_children_ = std::make_unique<ScopedOverviewHideWindows>( std::move(transient_children_to_hide), /*forced_hidden=*/true); } + + aura::client::GetTransientWindowClient()->AddObserver(this); } ScopedOverviewTransformWindow::~ScopedOverviewTransformWindow() { - for (auto* transient : wm::GetTransientTreeIterator(window_)) + for (auto* transient : wm::GetTransientTreeIterator(window_)) { transient->ClearProperty(kIsShowingInOverviewKey); - DCHECK(!window_->GetProperty(kIsShowingInOverviewKey)); + DCHECK(targeting_policy_map_.contains(transient)); + auto it = targeting_policy_map_.find(transient); + transient->SetEventTargetingPolicy(it->second); + targeting_policy_map_.erase(it); + } - window_->SetEventTargetingPolicy(original_event_targeting_policy_); UpdateMask(/*show=*/false); StopObservingImplicitAnimations(); + aura::client::GetTransientWindowClient()->RemoveObserver(this); } // static @@ -437,17 +440,6 @@ } } -void ScopedOverviewTransformWindow::CloseWidget() { - aura::Window* parent_window = ::wm::GetTransientRoot(window_); - if (parent_window) - wm::CloseWidgetForWindow(parent_window); -} - -// static -void ScopedOverviewTransformWindow::SetImmediateCloseForTests() { - immediate_close_for_tests = true; -} - void ScopedOverviewTransformWindow::EnsureVisible() { original_opacity_ = 1.f; } @@ -502,10 +494,45 @@ overview_item_->OnDragAnimationCompleted(); } +void ScopedOverviewTransformWindow::OnTransientChildWindowAdded( + aura::Window* parent, + aura::Window* transient_child) { + if (parent != window_ && !::wm::HasTransientAncestor(parent, window_)) + return; + + DCHECK(!targeting_policy_map_.contains(transient_child)); + targeting_policy_map_[transient_child] = + transient_child->event_targeting_policy(); + transient_child->SetEventTargetingPolicy(aura::EventTargetingPolicy::kNone); +} + +void ScopedOverviewTransformWindow::OnTransientChildWindowRemoved( + aura::Window* parent, + aura::Window* transient_child) { + if (parent != window_ && !::wm::HasTransientAncestor(parent, window_)) + return; + + DCHECK(targeting_policy_map_.contains(transient_child)); + auto it = targeting_policy_map_.find(transient_child); + transient_child->SetEventTargetingPolicy(it->second); + targeting_policy_map_.erase(it); +} + gfx::Rect ScopedOverviewTransformWindow::GetMaskBoundsForTesting() const { if (!mask_) return gfx::Rect(); return mask_->layer()->bounds(); } +void ScopedOverviewTransformWindow::CloseWidget() { + aura::Window* parent_window = ::wm::GetTransientRoot(window_); + if (parent_window) + wm::CloseWidgetForWindow(parent_window); +} + +// static +void ScopedOverviewTransformWindow::SetImmediateCloseForTests() { + immediate_close_for_tests = true; +} + } // namespace ash
diff --git a/ash/wm/overview/scoped_overview_transform_window.h b/ash/wm/overview/scoped_overview_transform_window.h index d414285..3119e2b 100644 --- a/ash/wm/overview/scoped_overview_transform_window.h +++ b/ash/wm/overview/scoped_overview_transform_window.h
@@ -14,6 +14,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" +#include "ui/aura/client/transient_window_client_observer.h" #include "ui/compositor/layer_animation_observer.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" @@ -39,7 +40,8 @@ // fit in certain bounds. The window's state is restored when this object is // destroyed. class ASH_EXPORT ScopedOverviewTransformWindow - : public ui::ImplicitAnimationObserver { + : public ui::ImplicitAnimationObserver, + public aura::client::TransientWindowClientObserver { public: // Overview windows have certain properties if their aspect ratio exceedes a // threshold. This enum keeps track of which category the window falls into, @@ -155,6 +157,12 @@ void OnLayerAnimationStarted(ui::LayerAnimationSequence* sequence) override; void OnImplicitAnimationsCompleted() override; + // aura::client::TransientWindowClientObserver: + void OnTransientChildWindowAdded(aura::Window* parent, + aura::Window* transient_child) override; + void OnTransientChildWindowRemoved(aura::Window* parent, + aura::Window* transient_child) override; + aura::Window* window() const { return window_; } GridWindowFillMode type() const { return type_; } @@ -191,9 +199,6 @@ // The original opacity of the window before entering overview mode. float original_opacity_; - // For the duration of this object |window_| event targeting policy will be - // sent to NONE. Store the original so we can change it back when destroying - // this object. aura::EventTargetingPolicy original_event_targeting_policy_; // Specifies how the window is laid out in the grid. @@ -213,6 +218,12 @@ // while in overview. std::unique_ptr<WindowMask> mask_; + // For the duration of this object |window_| and its transient childrens' + // event targeting policy will be sent to NONE. Store the originals so we can + // change it back when destroying |this|. + base::flat_map<aura::Window*, aura::EventTargetingPolicy> + targeting_policy_map_; + // The original mask layer of the window before entering overview mode. ui::Layer* original_mask_layer_ = nullptr;
diff --git a/ash/wm/overview/scoped_overview_transform_window_unittest.cc b/ash/wm/overview/scoped_overview_transform_window_unittest.cc index 9e677d04..b4e25bb 100644 --- a/ash/wm/overview/scoped_overview_transform_window_unittest.cc +++ b/ash/wm/overview/scoped_overview_transform_window_unittest.cc
@@ -281,6 +281,62 @@ EXPECT_TRUE(child2->transform().IsIdentity()); } +// Tests that the event targeting policies of a given window and transient +// descendants gets set as expected. +TEST_F(ScopedOverviewTransformWindowTest, EventTargetingPolicy) { + using etp = aura::EventTargetingPolicy; + + // Helper for creating popups that will be transients for testing. + auto create_popup = [this] { + std::unique_ptr<aura::Window> popup = + CreateTestWindow(gfx::Rect(10, 10), aura::client::WINDOW_TYPE_POPUP); + popup->SetEventTargetingPolicy(etp::kTargetAndDescendants); + return popup; + }; + + auto window = CreateTestWindow(gfx::Rect(200, 200)); + window->SetEventTargetingPolicy(etp::kTargetAndDescendants); + + auto transient = create_popup(); + auto transient1 = create_popup(); + auto transient2 = create_popup(); + ::wm::AddTransientChild(window.get(), transient.get()); + + { + // Tests that after creating the scoped object, the window and its current + // transient child have |kNone| targeting policy. + ScopedOverviewTransformWindow scoped_window(nullptr, window.get()); + EXPECT_EQ(etp::kNone, window->event_targeting_policy()); + EXPECT_EQ(etp::kNone, transient->event_targeting_policy()); + + // Tests that after adding transient children, one to the window itself and + // one to the current transient child, they will both have |kNone| targeting + // policy. + ::wm::AddTransientChild(window.get(), transient1.get()); + ::wm::AddTransientChild(transient.get(), transient2.get()); + EXPECT_EQ(etp::kNone, transient1->event_targeting_policy()); + EXPECT_EQ(etp::kNone, transient2->event_targeting_policy()); + + // Tests that adding a transient child which does not have |window| as its + // descendant does not have its targeting policy altered. + auto window2 = CreateTestWindow(gfx::Rect(200, 200)); + auto transient3 = create_popup(); + ::wm::AddTransientChild(window2.get(), transient3.get()); + EXPECT_EQ(etp::kTargetAndDescendants, transient3->event_targeting_policy()); + + // Tests that removing a transient child from |window| will reset its + // targeting policy. + ::wm::RemoveTransientChild(window.get(), transient1.get()); + EXPECT_EQ(etp::kTargetAndDescendants, transient1->event_targeting_policy()); + } + + // Tests that when the scoped object is destroyed, the targeting policies all + // get reset. + EXPECT_EQ(etp::kTargetAndDescendants, window->event_targeting_policy()); + EXPECT_EQ(etp::kTargetAndDescendants, transient->event_targeting_policy()); + EXPECT_EQ(etp::kTargetAndDescendants, transient2->event_targeting_policy()); +} + class ScopedOverviewTransformWindowWithMaskTest : public ScopedOverviewTransformWindowTest { public:
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc index ab685ca3..75119626 100644 --- a/ash/wm/splitview/split_view_controller.cc +++ b/ash/wm/splitview/split_view_controller.cc
@@ -15,9 +15,6 @@ #include "ash/screen_util.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" -#include "ash/strings/grit/ash_strings.h" -#include "ash/system/toast/toast_data.h" -#include "ash/system/toast/toast_manager.h" #include "ash/wm/desks/desks_controller.h" #include "ash/wm/mru_window_tracker.h" #include "ash/wm/overview/overview_controller.h" @@ -44,7 +41,6 @@ #include "ui/aura/window_delegate.h" #include "ui/base/class_property.h" #include "ui/base/hit_test.h" -#include "ui/base/l10n/l10n_util.h" #include "ui/compositor/layer.h" #include "ui/gfx/animation/slide_animation.h" #include "ui/gfx/animation/tween.h" @@ -80,10 +76,6 @@ // tablet_mode_window_drag_delegate.cc. constexpr int kDividerSnapDurationMs = 300; -// Toast data. -constexpr char kAppCannotSnapToastId[] = "split_view_app_cannot_snap"; -constexpr int kAppCannotSnapToastDurationMs = 2500; - // Histogram names that record presentation time of resize operation with // following conditions, a) single snapped window, empty overview, b) two // snapped windows, c) single snapped window and non empty overview. @@ -269,7 +261,9 @@ CurrentValueBetween(starting_position_, ending_position_); split_view_controller_->NotifyDividerPositionChanged(); split_view_controller_->UpdateSnappedWindowsAndDividerBounds(); - split_view_controller_->SetWindowsTransformDuringResizing(); + // Updating the window may stop animation. + if (is_animating()) + split_view_controller_->SetWindowsTransformDuringResizing(); } SplitViewController* split_view_controller_; @@ -667,14 +661,6 @@ } } -void SplitViewController::ShowAppCannotSnapToast() { - ash::ToastData toast( - kAppCannotSnapToastId, - l10n_util::GetStringUTF16(IDS_ASH_SPLIT_VIEW_CANNOT_SNAP), - kAppCannotSnapToastDurationMs, base::Optional<base::string16>()); - ash::Shell::Get()->toast_manager()->Show(toast); -} - void SplitViewController::EndSplitView(EndReason end_reason) { if (!InSplitViewMode()) return; @@ -688,8 +674,11 @@ const bool is_divider_animating = IsDividerAnimating(); if (is_resizing_ || is_divider_animating) { is_resizing_ = false; - if (is_divider_animating) - StopAndShoveAnimatedDivider(); + if (is_divider_animating) { + // Don't call StopAndShoveAnimatedDivider as it will call observers. + divider_snap_animation_->Stop(); + divider_position_ = divider_snap_animation_->ending_position(); + } EndResizeImpl(); }
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h index 9082203f..7a4a801 100644 --- a/ash/wm/splitview/split_view_controller.h +++ b/ash/wm/splitview/split_view_controller.h
@@ -141,10 +141,6 @@ void Resize(const gfx::Point& location_in_screen); void EndResize(const gfx::Point& location_in_screen); - // Displays a toast notifying users the application selected for split view is - // not compatible. - void ShowAppCannotSnapToast(); - // Ends the split view mode. void EndSplitView(EndReason end_reason = EndReason::kNormal);
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc index 16160ee4..3f73975b 100644 --- a/ash/wm/splitview/split_view_utils.cc +++ b/ash/wm/splitview/split_view_utils.cc
@@ -10,12 +10,16 @@ #include "ash/public/cpp/ash_switches.h" #include "ash/screen_util.h" #include "ash/shell.h" +#include "ash/strings/grit/ash_strings.h" +#include "ash/system/toast/toast_data.h" +#include "ash/system/toast/toast_manager.h" #include "ash/wm/screen_pinning_controller.h" #include "ash/wm/splitview/split_view_constants.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "ash/wm/window_state.h" #include "base/command_line.h" #include "ui/aura/window_delegate.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/layer_animator.h" @@ -52,6 +56,10 @@ constexpr float kHighlightOpacity = 0.3f; constexpr float kPreviewAreaHighlightOpacity = 0.18f; +// Toast data. +constexpr char kAppCannotSnapToastId[] = "split_view_app_cannot_snap"; +constexpr int kAppCannotSnapToastDurationMs = 2500; + // Gets the duration, tween type and delay before animation based on |type|. void GetAnimationValuesForType( SplitviewAnimationType type, @@ -268,6 +276,13 @@ return true; } +void ShowAppCannotSnapToast() { + ash::Shell::Get()->toast_manager()->Show(ash::ToastData( + kAppCannotSnapToastId, + l10n_util::GetStringUTF16(IDS_ASH_SPLIT_VIEW_CANNOT_SNAP), + kAppCannotSnapToastDurationMs, base::Optional<base::string16>())); +} + bool IsPhysicalLeftOrTop(SplitViewController::SnapPosition position) { DCHECK_NE(SplitViewController::NONE, position); return position == (IsCurrentScreenOrientationPrimary()
diff --git a/ash/wm/splitview/split_view_utils.h b/ash/wm/splitview/split_view_utils.h index fc276f1..4012a6a 100644 --- a/ash/wm/splitview/split_view_utils.h +++ b/ash/wm/splitview/split_view_utils.h
@@ -91,6 +91,10 @@ // tablet mode. ASH_EXPORT bool CanSnapInSplitview(aura::Window* window); +// Displays a toast notifying users the application selected for split view is +// not compatible. +ASH_EXPORT void ShowAppCannotSnapToast(); + ASH_EXPORT bool IsPhysicalLeftOrTop(SplitViewController::SnapPosition position); } // namespace ash
diff --git a/ash/wm/window_cycle_controller.cc b/ash/wm/window_cycle_controller.cc index b190d192..3eb695a7 100644 --- a/ash/wm/window_cycle_controller.cc +++ b/ash/wm/window_cycle_controller.cc
@@ -60,7 +60,7 @@ Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kAllDesks); // Window cycle list windows will handle showing their transient related // windows, so if a window in |window_list| has a transient root also in - // |window_list|, we can remove it as the tranisent root will handle showing + // |window_list|, we can remove it as the transient root will handle showing // the window. wm::RemoveTransientDescendants(&window_list);
diff --git a/base/allocator/partition_allocator/address_space_randomization_unittest.cc b/base/allocator/partition_allocator/address_space_randomization_unittest.cc index 3fe46ba6..395567a 100644 --- a/base/allocator/partition_allocator/address_space_randomization_unittest.cc +++ b/base/allocator/partition_allocator/address_space_randomization_unittest.cc
@@ -180,9 +180,10 @@ // Chi squared analysis for k = 2 (2, states: same/not-same) and one // degree of freedom (k - 1). double chi_squared = ChiSquared(m, kRepeats); - // For 1 degree of freedom this corresponds to 1 in a million. We are - // running ~8000 tests, so that would be surprising. - CHECK_GE(24, chi_squared); + // For k=2 probability of Chi^2 < 35 is p=3.338e-9. This condition is + // tested ~19000 times, so probability of it failing randomly per one + // base_unittests run is (1 - (1 - p) ^ 19000) ~= 6e-5. + CHECK_LE(chi_squared, 35.0); // If the predictor bit is a fixed 0 or 1 then it makes no sense to // repeat the test with a different age. if (predictor_bit < 0) @@ -191,9 +192,6 @@ } } -// TODO(crbug.com/811881): These are flaky on Fuchsia -#if !defined(OS_FUCHSIA) - // Tests are fairly slow, so give each random bit its own test. #define TEST_RANDOM_BIT(BIT) \ TEST(AddressSpaceRandomizationTest, RandomBitCorrelations##BIT) { \ @@ -242,8 +240,6 @@ // No platforms have more than 48 address bits. #endif // defined(ARCH_CPU_64_BITS) -#endif // defined(OS_FUCHSIA) - #undef TEST_RANDOM_BIT } // namespace base
diff --git a/base/files/file_path.cc b/base/files/file_path.cc index 45f78a2..f1ad6880 100644 --- a/base/files/file_path.cc +++ b/base/files/file_path.cc
@@ -1260,7 +1260,6 @@ // GetHFSDecomposedForm() returns an empty string in an error case. if (hfs1.empty() || hfs2.empty()) { - NOTREACHED(); ScopedCFTypeRef<CFStringRef> cfstring1( CFStringCreateWithBytesNoCopy( NULL, @@ -1277,6 +1276,20 @@ kCFStringEncodingUTF8, false, kCFAllocatorNull)); + // If neither GetHFSDecomposedForm nor CFStringCreateWithBytesNoCopy + // succeed, fall back to strcmp. This can occur when the input string is + // invalid UTF-8. + if (!cfstring1 || !cfstring2) { + int comparison = + memcmp(string1.as_string().c_str(), string2.as_string().c_str(), + std::min(string1.length(), string2.length())); + if (comparison < 0) + return -1; + if (comparison > 0) + return 1; + return 0; + } + return CFStringCompare(cfstring1, cfstring2, kCFCompareCaseInsensitive);
diff --git a/base/files/file_path_unittest.cc b/base/files/file_path_unittest.cc index afe70916..c3cbc9a 100644 --- a/base/files/file_path_unittest.cc +++ b/base/files/file_path_unittest.cc
@@ -1317,6 +1317,17 @@ EXPECT_TRUE(observed.empty()); } } + +TEST_F(FilePathTest, CompareIgnoreCaseWithInvalidInput) { + const FilePath::CharType* cases[] = { + FPL("\xc3\x28"), FPL("\xe2\x82\x28"), FPL("\xe2\x28\xa1"), + FPL("\xf0\x28\x8c\xbc"), FPL("\xf0\x28\x8c\x28"), + }; + for (auto* invalid_input : cases) { + // All example inputs will be greater than the string "fixed". + EXPECT_EQ(FilePath::CompareIgnoreCase(invalid_input, FPL("fixed")), 1); + } +} #endif } // namespace base
diff --git a/base/i18n/time_formatting.h b/base/i18n/time_formatting.h index dfbfeea6..64b09964 100644 --- a/base/i18n/time_formatting.h +++ b/base/i18n/time_formatting.h
@@ -47,10 +47,6 @@ DATE_FORMAT_MONTH_WEEKDAY_DAY, }; -// TODO(derat@chromium.org): Update all of these functions to return boolean -// "success" values and use out-params for formatted strings: -// http://crbug.com/698802 - // Returns the time of day, e.g., "3:07 PM". BASE_I18N_EXPORT string16 TimeFormatTimeOfDay(const Time& time);
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.cc b/base/sampling_heap_profiler/poisson_allocation_sampler.cc index 16bb6fe..515cf6c 100644 --- a/base/sampling_heap_profiler/poisson_allocation_sampler.cc +++ b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
@@ -7,6 +7,7 @@ #include <algorithm> #include <atomic> #include <cmath> +#include <memory> #include <utility> #include "base/allocator/allocator_shim.h" @@ -319,9 +320,8 @@ CHECK_EQ(nullptr, instance_); instance_ = this; Init(); - auto sampled_addresses = std::make_unique<LockFreeAddressHashSet>(64); - g_sampled_addresses_set = sampled_addresses.get(); - sampled_addresses_stack_.push_back(std::move(sampled_addresses)); + auto* sampled_addresses = new LockFreeAddressHashSet(64); + g_sampled_addresses_set.store(sampled_addresses, std::memory_order_release); } // static @@ -509,16 +509,15 @@ std::make_unique<LockFreeAddressHashSet>(current_set.buckets_count() * 2); new_set->Copy(current_set); // Atomically switch all the new readers to the new set. - g_sampled_addresses_set = new_set.get(); - // We still have to keep all the old maps alive to resolve the theoretical - // race with readers in |RecordFree| that have already obtained the map, + g_sampled_addresses_set.store(new_set.release(), std::memory_order_release); + // We leak the older set because we still have to keep all the old maps alive + // as there might be reader threads that have already obtained the map, // but haven't yet managed to access it. - sampled_addresses_stack_.push_back(std::move(new_set)); } // static LockFreeAddressHashSet& PoissonAllocationSampler::sampled_addresses_set() { - return *g_sampled_addresses_set.load(std::memory_order_relaxed); + return *g_sampled_addresses_set.load(std::memory_order_acquire); } // static
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.h b/base/sampling_heap_profiler/poisson_allocation_sampler.h index 7709e02..f0751e4 100644 --- a/base/sampling_heap_profiler/poisson_allocation_sampler.h +++ b/base/sampling_heap_profiler/poisson_allocation_sampler.h
@@ -5,7 +5,6 @@ #ifndef BASE_SAMPLING_HEAP_PROFILER_POISSON_ALLOCATION_SAMPLER_H_ #define BASE_SAMPLING_HEAP_PROFILER_POISSON_ALLOCATION_SAMPLER_H_ -#include <memory> #include <vector> #include "base/base_export.h" @@ -110,7 +109,6 @@ void BalanceAddressesHashSet(); Lock mutex_; - std::vector<std::unique_ptr<LockFreeAddressHashSet>> sampled_addresses_stack_; std::vector<SamplesObserver*> observers_; static PoissonAllocationSampler* instance_;
diff --git a/build/android/gyp/main_dex_list.py b/build/android/gyp/main_dex_list.py index 520be39..2435859 100755 --- a/build/android/gyp/main_dex_list.py +++ b/build/android/gyp/main_dex_list.py
@@ -32,8 +32,8 @@ parser.add_argument('--inputs', help='JARs for which a main dex list should be ' 'generated.') - parser.add_argument( - '--r8-path', required=True, help='Path to the r8 executable.') + parser.add_argument('--proguard-path', required=True, + help='Path to the proguard executable.') parser.add_argument('--negative-main-dex-globs', help='GN-list of globs of .class names (e.g. org/chromium/foo/Bar.class) ' 'that will fail the build if they match files in the main dex.') @@ -55,24 +55,13 @@ args.negative_main_dex_globs) proguard_cmd = [ - 'java', - '-jar', - args.r8_path, - '--classfile', - '--lib', - args.shrinked_android_path, + 'java', '-jar', args.proguard_path, + '-forceprocessing', + '-dontwarn', '-dontoptimize', '-dontobfuscate', '-dontpreverify', + '-libraryjars', args.shrinked_android_path, ] - for m in args.main_dex_rules_paths: - proguard_cmd.extend(['--pg-conf', m]) - - proguard_flags = [ - '-forceprocessing', - '-dontwarn', - '-dontoptimize', - '-dontobfuscate', - '-dontpreverify', - ] + proguard_cmd.extend(['-include', m]) main_dex_list_cmd = [ 'java', '-cp', args.dx_path, @@ -94,24 +83,17 @@ proguard_cmd, main_dex_list_cmd, ] - if args.negative_main_dex_globs: input_strings += args.negative_main_dex_globs - for glob in args.negative_main_dex_globs: - # Globs come with 1 asterix, but we want 2 to match subpackages. - proguard_flags.append('-checkdiscard class ' + - glob.replace('*', '**').replace('/', '.')) output_paths = [ args.main_dex_list_path, ] - def _LineLengthHelperForOnStaleMd5(): - _OnStaleMd5(proguard_cmd, proguard_flags, main_dex_list_cmd, args.paths, - args.main_dex_list_path) - build_utils.CallAndWriteDepfileIfStale( - _LineLengthHelperForOnStaleMd5, + lambda: _OnStaleMd5(proguard_cmd, main_dex_list_cmd, args.paths, + args.main_dex_list_path, + args.negative_main_dex_globs), args, input_paths=input_paths, input_strings=input_strings, @@ -122,22 +104,38 @@ return 0 -def _OnStaleMd5(proguard_cmd, proguard_flags, main_dex_list_cmd, paths, - main_dex_list_path): +def _CheckForUnwanted(kept_classes, proguard_cmd, negative_main_dex_globs): + # Check if ProGuard kept any unwanted classes. + found_unwanted_classes = sorted( + p for p in kept_classes + if build_utils.MatchesGlob(p, negative_main_dex_globs)) + + if found_unwanted_classes: + first_class = found_unwanted_classes[0].replace( + '.class', '').replace('/', '.') + proguard_cmd += ['-whyareyoukeeping', 'class', first_class, '{}'] + output = build_utils.CheckOutput( + proguard_cmd, print_stderr=False, + stdout_filter=proguard_util.ProguardOutputFilter()) + raise Exception( + ('Found classes that should not be in the main dex:\n {}\n\n' + 'Here is the -whyareyoukeeping output for {}: \n{}').format( + '\n '.join(found_unwanted_classes), first_class, output)) + + +def _OnStaleMd5(proguard_cmd, main_dex_list_cmd, paths, main_dex_list_path, + negative_main_dex_globs): + paths_arg = ':'.join(paths) main_dex_list = '' try: with tempfile.NamedTemporaryFile(suffix='.jar') as temp_jar: # Step 1: Use ProGuard to find all @MainDex code, and all code reachable # from @MainDex code (recursive). - proguard_cmd += ['--output', temp_jar.name] - with tempfile.NamedTemporaryFile() as proguard_flags_file: - for flag in proguard_flags: - proguard_flags_file.write(flag + '\n') - proguard_flags_file.flush() - proguard_cmd += ['--pg-conf', proguard_flags_file.name] - for injar in paths: - proguard_cmd.append(injar) - build_utils.CheckOutput(proguard_cmd, print_stderr=False) + proguard_cmd += [ + '-injars', paths_arg, + '-outjars', temp_jar.name + ] + build_utils.CheckOutput(proguard_cmd, print_stderr=False) # Record the classes kept by ProGuard. Not used by the build, but useful # for debugging what classes are kept by ProGuard vs. MainDexListBuilder. @@ -146,9 +144,18 @@ with open(main_dex_list_path + '.partial', 'w') as f: f.write('\n'.join(kept_classes) + '\n') + if negative_main_dex_globs: + # Perform assertions before MainDexListBuilder because: + # a) MainDexListBuilder is not recursive, so being included by it isn't + # a huge deal. + # b) Errors are much more actionable. + _CheckForUnwanted(kept_classes, proguard_cmd, negative_main_dex_globs) + # Step 2: Expand inclusion list to all classes referenced by the .class # files of kept classes (non-recursive). - main_dex_list_cmd += [temp_jar.name, ':'.join(paths)] + main_dex_list_cmd += [ + temp_jar.name, paths_arg + ] main_dex_list = build_utils.CheckOutput(main_dex_list_cmd) except build_utils.CalledProcessError as e:
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index aff8874..010202bd 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -1302,11 +1302,18 @@ # http://crbug.com/725224. Fix for bots running out of memory. pool = "//build/toolchain:link_pool($default_toolchain)" + if (defined(invoker.proguard_jar_path)) { + _proguard_jar_path = invoker.proguard_jar_path + } else { + _proguard_jar_path = _default_proguard_jar_path + } + _shrinked_android = "$android_sdk_build_tools/lib/shrinkedAndroid.jar" _dx = "$android_sdk_build_tools/lib/dx.jar" inputs = [ _main_dex_rules, _dx, + _proguard_jar_path, _shrinked_android, ] @@ -1325,8 +1332,8 @@ rebase_path(_main_dex_list_path, root_build_dir), "--main-dex-rules-path", rebase_path(_main_dex_rules, root_build_dir), - "--r8-path", - rebase_path("//third_party/r8/lib/r8.jar", root_build_dir), + "--proguard-path", + rebase_path(_proguard_jar_path, root_build_dir), ] if (defined(invoker.extra_main_dex_proguard_config)) { @@ -1351,11 +1358,11 @@ if (defined(invoker.input_jar_classpath)) { inputs += [ invoker.build_config ] args += [ "--inputs=@FileArg(${invoker.input_jar_classpath})" ] - } else { - inputs += _dexing_jars - if (_dexing_jars != []) { - args += rebase_path(_dexing_jars, root_build_dir) - } + } + + inputs += _dexing_jars + if (_dexing_jars != []) { + args += rebase_path(_dexing_jars, root_build_dir) } } }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index 9508d9c..e3c810e 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -31,7 +31,7 @@ _sanitizer_runtimes = [ "$clang_base_path/lib/clang/$clang_version/lib/linux/libclang_rt.ubsan_standalone-$_sanitizer_arch-android.so" ] } -if (is_hwasan) { +if (is_hwasan && !hwasan_platform) { _sanitizer_runtimes = [ "$clang_base_path/lib/clang/$clang_version/lib/linux/libclang_rt.hwasan-$_sanitizer_arch-android.so" ] }
diff --git a/build/config/sanitizers/BUILD.gn b/build/config/sanitizers/BUILD.gn index 993d8e7..d33e818b 100644 --- a/build/config/sanitizers/BUILD.gn +++ b/build/config/sanitizers/BUILD.gn
@@ -436,6 +436,9 @@ config("hwasan_flags") { if (is_hwasan) { cflags = [ "-fsanitize=hwaddress" ] + if (hwasan_platform) { + cflags += [ "-fsanitize-hwaddress-abi=platform" ] + } } }
diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni index 8cff7271..2ac235b3 100644 --- a/build/config/sanitizers/sanitizers.gni +++ b/build/config/sanitizers/sanitizers.gni
@@ -16,6 +16,10 @@ # See http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html is_hwasan = false + # Specify whether to target the platform's copy of the HWASan runtime, + # rather than one bundled with the application. + hwasan_platform = false + # Compile for Leak Sanitizer to find leaks. is_lsan = false @@ -123,6 +127,7 @@ is_asan = false is_cfi = false is_hwasan = false + hwasan_platform = false is_lsan = false is_msan = false is_tsan = false @@ -245,6 +250,9 @@ "Chromium mac_clang_x64 toolchain on iOS distribution. Please set " + "the argument value to false.") +assert(!hwasan_platform || is_hwasan, + "hwasan_platform requires is_hwasan to be set") + # Use these lists of configs to disable instrumenting code that is part of a # fuzzer, but which isn't being targeted (such as libprotobuf-mutator, *.pb.cc # and libprotobuf when they are built as part of a proto fuzzer). Adding or
diff --git a/build/partitioned_shared_library.gni b/build/partitioned_shared_library.gni index 33b7f1c..81e372f 100644 --- a/build/partitioned_shared_library.gni +++ b/build/partitioned_shared_library.gni
@@ -27,18 +27,18 @@ # The template instantiates targets for the base library, as well as each # specified partition, based on the root target name. Example: # -# - monochrome_base (base library) -# - monochrome_base_foo (partition library for feature 'foo') -# - monochrome_base_bar (partition library for feature 'bar') +# - monochrome (base library) +# - monochrome_foo (partition library for feature 'foo') +# - monochrome_bar (partition library for feature 'bar') # # The base library is placed in the root output directory, but additional # feature libraries are placed in a subdirectory named according to the base # library. This avoids name collisions, since feature library names are not # sensitive to the base library to which they are paired. Example: # -# - out/libmonochrome_base.so -# - out/monochrome_base_partitions/libfoo.so -# - out/monochrome_base_partitions/libbar.so +# - out/libmonochrome.so +# - out/monochrome_partitions/libfoo.so +# - out/monochrome_partitions/libbar.so # # This template uses shared_library's default configurations. #
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn index 7b277dc4..1f6534b 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn
@@ -6,7 +6,6 @@ import("//build/config/compiler/compiler.gni") import("//build/config/compiler/pgo/pgo.gni") import("//build/config/features.gni") -import("//build/config/linux/pangocairo/pangocairo.gni") import("//build/config/locales.gni") import("//build/config/sanitizers/sanitizers.gni") import("//build/config/ui.gni")
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 1eec119..6ab78ed 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -425,7 +425,7 @@ "//components/signin/core/browser:signin_enums_javagen", "//components/supervised_user_error_page:enums_srcjar", "//components/ui_metrics:ui_metrics_enums_java", - "//chrome/browser/notifications/scheduler:jni_enums", + "//chrome/browser/notifications/scheduler/public:jni_enums", "//chrome/browser/ui:tab_model_enums_java", "//net:effective_connection_type_java", ":vr_build_config", @@ -1147,44 +1147,26 @@ } } -# This template creates a native library for Chrome's APK or bundle. -template("libchrome_apk_or_bundle_tmpl") { - chrome_common_shared_library(target_name) { - forward_variables_from(invoker, "*", [ "is_bundle" ]) - sources = [ - "../browser/android/chrome_entry_point.cc", - chrome_jni_registration_header, - ] - deps = [ - ":chrome_jni_registration($default_toolchain)", - ] +# Chrome APK's native library. +chrome_common_shared_library("libchrome") { + sources = [ + "../browser/android/chrome_entry_point.cc", + chrome_jni_registration_header, + ] + deps = [ + ":chrome_jni_registration($default_toolchain)", + ] - # Include appropriate factories for native feature modules if necessary. - if (enable_vr) { - if (defined(invoker.is_bundle) && invoker.is_bundle) { - if (modularize_vr_native) { - deps += [ "//chrome/browser/android/vr:ui_module_factory" ] - } else { - deps += [ "//chrome/browser/android/vr:ui_module_dummy_factory" ] - } - } else { - deps += [ "//chrome/browser/android/vr:ui_default_factory" ] - } + # Include appropriate factories for native feature modules if necessary. + if (enable_vr) { + if (modularize_vr_native) { + deps += [ "//chrome/browser/android/vr:ui_module_factory" ] + } else { + deps += [ "//chrome/browser/android/vr:ui_module_dummy_factory" ] } } } -# Chrome APK's native library. -libchrome_apk_or_bundle_tmpl("libchrome") { - is_bundle = false -} - -# Chrome bundle's base module native library. This is currently the same as the -# APK version, but will diverge when code moves into feature modules. -libchrome_apk_or_bundle_tmpl("libchrome_base") { - is_bundle = true -} - chrome_common_shared_library("libchromefortest") { testonly = true sources = [ @@ -1236,25 +1218,18 @@ # is_monochrome: If true, generate Monochrome targets rather than Chrome. # is_trichrome: Optionally generate Trichrome targets that use monochrome # library targets but don't include webview resources. - # is_bundle: If true, generate resources for bundles rather than APK. template("resource_packaging") { _is_monochrome = invoker.is_monochrome - _is_bundle = invoker.is_bundle _is_trichrome = defined(invoker.is_trichrome) && invoker.is_trichrome if (_is_trichrome) { - _type = "trichrome_chrome" + _variant = "trichrome_chrome" } else if (_is_monochrome) { - _type = "monochrome" + _variant = "monochrome" } else { - _type = "chrome" + _variant = "chrome" } - if (_is_bundle) { - _output_type = "bundle" - } else { - _output_type = "apk" - } - _variant = "${_type}_${_output_type}" + _variant += "_apk" if (enable_resource_whitelist_generation) { if (_is_trichrome || _is_monochrome) { @@ -1271,13 +1246,8 @@ } else { _target_prefix = "lib" } - if (_is_bundle) { - _suffix = bundle_library_suffix - } else { - _suffix = "" - } - _lib_path = "/lib.unstripped/lib" + _lib + _suffix + shlib_extension - _lib_target = _target_prefix + _lib + _suffix + _lib_path = "/lib.unstripped/lib" + _lib + shlib_extension + _lib_target = _target_prefix + _lib generate_resource_whitelist(_resource_whitelist_target) { _fat_lib_toolchain = "" @@ -1390,7 +1360,7 @@ } } - # This target is separate from monochrome_pak_assets because it does not + # This target is separate from monochrome_apk_pak_assets because it does not # disable compression. android_assets("${_variant}_locale_pak_assets") { renaming_sources = [] @@ -1430,43 +1400,20 @@ # resource whitelist is derived from the native library. resource_packaging("chrome_apk_pak_assets") { is_monochrome = false - is_bundle = false } resource_packaging("monochrome_apk_pak_assets") { is_monochrome = true - is_bundle = false } resource_packaging("trichrome_chrome_apk_pak_assets") { is_monochrome = false is_trichrome = true - is_bundle = false - } - resource_packaging("chrome_bundle_pak_assets") { - is_monochrome = false - is_bundle = true - } - resource_packaging("monochrome_bundle_pak_assets") { - is_monochrome = true - is_bundle = true - } - resource_packaging("trichrome_chrome_bundle_pak_assets") { - is_monochrome = false - is_bundle = true - is_trichrome = true - } - - # TODO(cjgrant): Remove this temporary alias after downstream renaming lands. - java_group("chrome_public_pak_assets") { - deps = [ - ":chrome_apk_pak_assets", - ] } } # current_toolchain == host_toolchain # Monochrome equivalent of Chrome's APK or bundle library template. template("libmonochrome_apk_or_bundle_tmpl") { chrome_common_shared_library(target_name) { - forward_variables_from(invoker, "*", [ "is_bundle" ]) + forward_variables_from(invoker, "*") sources = [ "../browser/android/monochrome_entry_point.cc", ] @@ -1477,14 +1424,10 @@ # Include appropriate factories for native feature modules if necessary. if (enable_vr) { - if (defined(invoker.is_bundle) && invoker.is_bundle) { - if (modularize_vr_native) { - deps += [ "//chrome/browser/android/vr:ui_module_factory" ] - } else { - deps += [ "//chrome/browser/android/vr:ui_module_dummy_factory" ] - } + if (modularize_vr_native) { + deps += [ "//chrome/browser/android/vr:ui_module_factory" ] } else { - deps += [ "//chrome/browser/android/vr:ui_default_factory" ] + deps += [ "//chrome/browser/android/vr:ui_module_dummy_factory" ] } } @@ -1496,12 +1439,6 @@ current_toolchain == android_secondary_abi_toolchain) { # Monochrome APK native library. libmonochrome_apk_or_bundle_tmpl("monochrome") { - is_bundle = false - } - - # Monochrome bundle native library. - libmonochrome_apk_or_bundle_tmpl("monochrome_base") { - is_bundle = true } if (android_64bit_target_cpu) { @@ -1510,19 +1447,10 @@ ":monochrome_64($android_secondary_abi_toolchain)", ] } - group("monochrome_64_base_secondary_abi_lib") { - public_deps = [ - ":monochrome_64_base($android_secondary_abi_toolchain)", - ] - } } } else { # 64-bit browser library targets (APK and bundle). libmonochrome_apk_or_bundle_tmpl("monochrome_64") { - is_bundle = false - } - libmonochrome_apk_or_bundle_tmpl("monochrome_64_base") { - is_bundle = true } # 32-bit browser library alias targets, pulled in by 64-bit WebView builds. @@ -1531,11 +1459,6 @@ ":monochrome($android_secondary_abi_toolchain)", ] } - group("monochrome_base_secondary_abi_lib") { - public_deps = [ - ":monochrome_base($android_secondary_abi_toolchain)", - ] - } } # Java libraries that go into each public chrome APK and base module. The chrome @@ -1663,7 +1586,7 @@ "enable_multidex", ]) - deps = _chrome_public_shared_deps + deps = _chrome_public_shared_deps + [ ":chrome_apk_pak_assets" ] if (_is_modern) { android_manifest = chrome_modern_public_android_manifest @@ -1673,19 +1596,11 @@ android_manifest_dep = ":chrome_public_android_manifest" } - if (invoker.target_type == "android_app_bundle_module") { - deps += [ ":chrome_${bundle_pak_asset_type}_pak_assets" ] - _suffix = bundle_library_suffix - } else { - deps += [ ":chrome_apk_pak_assets" ] - _suffix = "" - } - - shared_libraries = [ ":libchrome${_suffix}" ] + shared_libraries = [ ":libchrome" ] add_unwind_tables_in_apk = _add_unwind_tables_in_chrome_32bit_apk && target_cpu == "arm" if (add_unwind_tables_in_apk) { - shared_library_for_unwind_asset = "chrome${_suffix}" + shared_library_for_unwind_asset = "chrome" } # Android supports webp transparent resources properly since API level 18, @@ -1778,18 +1693,14 @@ variables += trichrome_jinja_variables } else { _arch = "" - _lib_suffix = "" if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) { _arch = "_64" } - if (invoker.target_type == "android_app_bundle_module") { - _lib_suffix = bundle_library_suffix - } input = "java/AndroidManifest_monochrome.xml" includes += [ "//android_webview/apk/java/AndroidManifest.xml" ] variables += monochrome_android_manifest_jinja_variables + [ "target_sdk_version=$android_sdk_version", - "webview_library=libmonochrome${_arch}${_lib_suffix}.so", + "webview_library=libmonochrome${_arch}.so", "include_arcore_manifest_flag=true", ] } @@ -2678,9 +2589,9 @@ "java/src/org/chromium/chrome/browser/ssl/CaptivePortalHelper.java", "java/src/org/chromium/chrome/browser/ssl/SecurityStateModel.java", "java/src/org/chromium/chrome/browser/subresource_filter/TestSubresourceFilterPublisher.java", - "java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java", - "java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java", "java/src/org/chromium/chrome/browser/suggestions/SuggestionsEventReporterBridge.java", + "java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSites.java", + "java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java", "java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java", "java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java", "java/src/org/chromium/chrome/browser/tab/Tab.java",
diff --git a/chrome/android/chrome_common_shared_library.gni b/chrome/android/chrome_common_shared_library.gni index 65b1aae..9a8346d 100644 --- a/chrome/android/chrome_common_shared_library.gni +++ b/chrome/android/chrome_common_shared_library.gni
@@ -10,13 +10,9 @@ import("//chrome/android/features/dynamic_feature_modules.gni") import("//device/vr/buildflags/buildflags.gni") -# These variables control whether app bundles use the same native libraries as -# the APK, or bundle-specific versions. The bundle-specific versions may -# diverge when code is moved from the base library into dynamic feature -# modules. The variables can be removed when the bundle-specific lib -# configuration has stabilized. -bundle_library_suffix = "_base" -bundle_pak_asset_type = "bundle" +# TODO(cjgrant): Remove these variables once downstream stops using them. +bundle_library_suffix = "" +apk_pak_asset_type = "apk" # This value is set downstream for internal builds. if (!defined(default_chrome_orderfile)) {
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index 45c1745b..4b7e2668 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -1439,12 +1439,8 @@ "java/src/org/chromium/chrome/browser/subresource_filter/TestSubresourceFilterPublisher.java", "java/src/org/chromium/chrome/browser/suggestions/DestructionObserver.java", "java/src/org/chromium/chrome/browser/suggestions/ImageFetcher.java", - "java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java", - "java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java", "java/src/org/chromium/chrome/browser/suggestions/NavigationRecorder.java", "java/src/org/chromium/chrome/browser/suggestions/OfflinableSuggestion.java", - "java/src/org/chromium/chrome/browser/suggestions/SiteSection.java", - "java/src/org/chromium/chrome/browser/suggestions/SiteSectionViewHolder.java", "java/src/org/chromium/chrome/browser/suggestions/SiteSuggestion.java", "java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java", "java/src/org/chromium/chrome/browser/suggestions/SuggestionsConfig.java", @@ -1457,16 +1453,20 @@ "java/src/org/chromium/chrome/browser/suggestions/SuggestionsRanker.java", "java/src/org/chromium/chrome/browser/suggestions/SuggestionsRecyclerView.java", "java/src/org/chromium/chrome/browser/suggestions/SuggestionsSheetVisibilityChangeObserver.java", - "java/src/org/chromium/chrome/browser/suggestions/SuggestionsTileView.java", "java/src/org/chromium/chrome/browser/suggestions/SuggestionsUiDelegate.java", "java/src/org/chromium/chrome/browser/suggestions/SuggestionsUiDelegateImpl.java", "java/src/org/chromium/chrome/browser/suggestions/ThumbnailGradient.java", - "java/src/org/chromium/chrome/browser/suggestions/Tile.java", - "java/src/org/chromium/chrome/browser/suggestions/TileGridLayout.java", - "java/src/org/chromium/chrome/browser/suggestions/TileGridViewHolder.java", - "java/src/org/chromium/chrome/browser/suggestions/TileGroup.java", - "java/src/org/chromium/chrome/browser/suggestions/TileGroupDelegateImpl.java", - "java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java", + "java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSites.java", + "java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java", + "java/src/org/chromium/chrome/browser/suggestions/tile/SiteSection.java", + "java/src/org/chromium/chrome/browser/suggestions/tile/SiteSectionViewHolder.java", + "java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java", + "java/src/org/chromium/chrome/browser/suggestions/tile/Tile.java", + "java/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayout.java", + "java/src/org/chromium/chrome/browser/suggestions/tile/TileGridViewHolder.java", + "java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java", + "java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java", + "java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java", "java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java", "java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java", "java/src/org/chromium/chrome/browser/survey/SurveyController.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni index 3668ba2..1c5f0d5f 100644 --- a/chrome/android/chrome_junit_test_java_sources.gni +++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -175,8 +175,7 @@ "junit/src/org/chromium/chrome/browser/signin/SigninPromoUtilTest.java", "junit/src/org/chromium/chrome/browser/snackbar/SnackbarCollectionUnitTest.java", "junit/src/org/chromium/chrome/browser/suggestions/SuggestionsImageFetcherTest.java", - "junit/src/org/chromium/chrome/browser/suggestions/TileGroupUnitTest.java", - "junit/src/org/chromium/chrome/browser/suggestions/TileGroupUnitTest.java", + "junit/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java", "junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java", "junit/src/org/chromium/chrome/browser/tab/TabAttributesTest.java", "junit/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni index 07a858e2..7a268f3b 100644 --- a/chrome/android/chrome_public_apk_tmpl.gni +++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -304,31 +304,26 @@ ] if (is_monochrome) { - if (invoker.target_type == "android_app_bundle_module") { - _suffix = bundle_library_suffix - } else { - _suffix = "" - } if (android_64bit_target_cpu) { # Build //android_webview:monochrome with the opposite bitness that # Chrome runs in. if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) { - shared_libraries = [ "//chrome/android:monochrome_64${_suffix}" ] + shared_libraries = [ "//chrome/android:monochrome_64" ] if (build_apk_secondary_abi && invoker.include_32_bit_webview) { - secondary_abi_shared_libraries = [ "//android_webview:monochrome_64${_suffix}($android_secondary_abi_toolchain)" ] + secondary_abi_shared_libraries = [ "//android_webview:monochrome_64($android_secondary_abi_toolchain)" ] } } else { - shared_libraries = [ "//android_webview:monochrome${_suffix}" ] + shared_libraries = [ "//android_webview:monochrome" ] if (build_apk_secondary_abi) { secondary_abi_shared_libraries = - [ "//chrome/android:monochrome${_suffix}_secondary_abi_lib" ] + [ "//chrome/android:monochrome_secondary_abi_lib" ] } } } else { - shared_libraries = [ "//chrome/android:monochrome${_suffix}" ] + shared_libraries = [ "//chrome/android:monochrome" ] } if (invoker.add_unwind_tables_in_apk) { - shared_library_for_unwind_asset = "monochrome${_suffix}" + shared_library_for_unwind_asset = "monochrome" } _deps += [ @@ -403,13 +398,7 @@ # Android N+ better supports multiple locales (https://crbug.com/780847). support_zh_hk = false - if (invoker.target_type == "android_app_bundle_module") { - _deps += [ - "//chrome/android:${_pak_prefix}_${bundle_pak_asset_type}_pak_assets", - ] - } else { - _deps += [ "//chrome/android:${_pak_prefix}_apk_pak_assets" ] - } + _deps += [ "//chrome/android:${_pak_prefix}_apk_pak_assets" ] if (_enable_multidex && invoker.target_type == "android_apk" && !defined(invoker.negative_main_dex_globs)) {
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni index af81503..bca0154 100644 --- a/chrome/android/chrome_test_java_sources.gni +++ b/chrome/android/chrome_test_java_sources.gni
@@ -420,8 +420,8 @@ "javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetTestRule.java", "javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetUiCaptureTest.java", "javatests/src/org/chromium/chrome/browser/suggestions/SuggestionsSheetVisibilityChangeObserverTest.java", - "javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java", - "javatests/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java", + "javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java", + "javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupTest.java", "javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java", "javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java", "javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java index 21ee7c3..b54c308 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
@@ -21,7 +21,6 @@ import org.chromium.chrome.browser.tabmodel.TabList; import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModelSelector; -import org.chromium.chrome.browser.toolbar.ToolbarManager; import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor; @@ -43,10 +42,10 @@ private final TabGridDialogCoordinator mTabGridDialogCoordinator; public GridTabSwitcherCoordinator(Context context, - ActivityLifecycleDispatcher lifecycleDispatcher, ToolbarManager toolbarManager, - TabModelSelector tabModelSelector, TabContentManager tabContentManager, - CompositorViewHolder compositorViewHolder, ChromeFullscreenManager fullscreenManager, - TabCreatorManager tabCreatorManager, Runnable backPress) { + ActivityLifecycleDispatcher lifecycleDispatcher, TabModelSelector tabModelSelector, + TabContentManager tabContentManager, CompositorViewHolder compositorViewHolder, + ChromeFullscreenManager fullscreenManager, TabCreatorManager tabCreatorManager, + Runnable backPress) { PropertyModel containerViewModel = new PropertyModel(TabListContainerProperties.ALL_KEYS); TabListMediator.GridCardOnClickListenerProvider gridCardOnClickListenerProvider; if (FeatureUtilities.isTabGroupsAndroidUiImprovementsEnabled()) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java index d3d3b0c..3d66b85b 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
@@ -27,9 +27,9 @@ "Downloaded_Enabled"); } return new GridTabSwitcherCoordinator(activity, activity.getLifecycleDispatcher(), - activity.getToolbarManager(), activity.getTabModelSelector(), - activity.getTabContentManager(), activity.getCompositorViewHolder(), - activity.getFullscreenManager(), activity, activity::onBackPressed); + activity.getTabModelSelector(), activity.getTabContentManager(), + activity.getCompositorViewHolder(), activity.getFullscreenManager(), activity, + activity::onBackPressed); } @Override
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java index 1d46ded..d4d87ca 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java
@@ -79,7 +79,6 @@ private static final String TAB1_TITLE = "Tab1"; private static final String TAB2_TITLE = "Tab2"; private static final String TAB3_TITLE = "Tab3"; - private static final String NEW_TITLE = "New title"; private static final int TAB1_ID = 456; private static final int TAB2_ID = 789; private static final int TAB3_ID = 123; @@ -115,8 +114,6 @@ ArgumentCaptor<TabModelSelectorObserver> mTabModelSelectorObserverCaptor; @Captor ArgumentCaptor<ChromeFullscreenManager.FullscreenListener> mFullscreenListenerCaptor; - @Captor - ArgumentCaptor<Tab> mTabCaptor; private Tab mTab1; private Tab mTab2;
diff --git a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected index e5fe8a4c..64d14ff 100644 --- a/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected +++ b/chrome/android/java/monochrome_public_bundle__base_bundle_module.AndroidManifest.expected
@@ -1161,9 +1161,7 @@ <meta-data android:name="android.content.APP_RESTRICTIONS" android:resource="@xml/app_restrictions"/> - <meta-data - android:name="com.android.webview.WebViewLibrary" - android:value="libmonochrome_base.so"/> + <meta-data android:name="com.android.webview.WebViewLibrary" android:value="libmonochrome.so"/> <meta-data android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME" android:value="org.chromium.chrome.browser.media.router.caf.CastOptionsProvider"/>
diff --git a/chrome/android/java/res/drawable/tile_view_hairline_border_background.xml b/chrome/android/java/res/drawable/tile_view_hairline_border_background.xml new file mode 100644 index 0000000..ba8478d --- /dev/null +++ b/chrome/android/java/res/drawable/tile_view_hairline_border_background.xml
@@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2019 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. --> + +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + <solid android:color="@color/modern_primary_color" /> + <stroke android:width="1dp" android:color="@color/hairline_stroke_color"/> +</shape>
diff --git a/chrome/android/java/res/layout/explore_sites_category_card_view.xml b/chrome/android/java/res/layout/explore_sites_category_card_view.xml index 011925b..05eae1d 100644 --- a/chrome/android/java/res/layout/explore_sites_category_card_view.xml +++ b/chrome/android/java/res/layout/explore_sites_category_card_view.xml
@@ -24,7 +24,7 @@ android:minHeight="@dimen/explore_sites_category_title_height" tools:text="Category" /> - <org.chromium.chrome.browser.suggestions.TileGridLayout + <org.chromium.chrome.browser.suggestions.tile.TileGridLayout android:id="@+id/category_sites" android:layout_width="wrap_content" android:layout_height="wrap_content"
diff --git a/chrome/android/java/res/layout/explore_sites_section.xml b/chrome/android/java/res/layout/explore_sites_section.xml index f5f341e..fbed051 100644 --- a/chrome/android/java/res/layout/explore_sites_section.xml +++ b/chrome/android/java/res/layout/explore_sites_section.xml
@@ -3,7 +3,7 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> -<org.chromium.chrome.browser.suggestions.TileGridLayout +<org.chromium.chrome.browser.suggestions.tile.TileGridLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"
diff --git a/chrome/android/java/res/layout/suggestions_site_tile_grid_modern.xml b/chrome/android/java/res/layout/suggestions_site_tile_grid_modern.xml index 11364d1..acc933ca 100644 --- a/chrome/android/java/res/layout/suggestions_site_tile_grid_modern.xml +++ b/chrome/android/java/res/layout/suggestions_site_tile_grid_modern.xml
@@ -3,7 +3,7 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> -<org.chromium.chrome.browser.suggestions.TileGridLayout +<org.chromium.chrome.browser.suggestions.tile.TileGridLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/tile_grid_layout" android:layout_width="match_parent"
diff --git a/chrome/android/java/res/layout/suggestions_tile_view.xml b/chrome/android/java/res/layout/suggestions_tile_view.xml index ba00700..971c8be 100644 --- a/chrome/android/java/res/layout/suggestions_tile_view.xml +++ b/chrome/android/java/res/layout/suggestions_tile_view.xml
@@ -4,7 +4,7 @@ found in the LICENSE file. --> <!-- A site suggestion tile. --> -<org.chromium.chrome.browser.suggestions.SuggestionsTileView +<org.chromium.chrome.browser.suggestions.tile.SuggestionsTileView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" @@ -16,4 +16,4 @@ android:layout_width="match_parent" android:layout_height="match_parent" layout="@layout/tile_view_modern" /> -</org.chromium.chrome.browser.suggestions.SuggestionsTileView> +</org.chromium.chrome.browser.suggestions.tile.SuggestionsTileView>
diff --git a/chrome/android/java/res/layout/suggestions_tile_view_condensed.xml b/chrome/android/java/res/layout/suggestions_tile_view_condensed.xml index 820b0c5..eebce13 100644 --- a/chrome/android/java/res/layout/suggestions_tile_view_condensed.xml +++ b/chrome/android/java/res/layout/suggestions_tile_view_condensed.xml
@@ -4,7 +4,7 @@ found in the LICENSE file. --> <!-- A site suggestion tile. --> -<org.chromium.chrome.browser.suggestions.SuggestionsTileView +<org.chromium.chrome.browser.suggestions.tile.SuggestionsTileView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" @@ -16,4 +16,4 @@ android:layout_width="match_parent" android:layout_height="match_parent" layout="@layout/tile_view_modern_condensed" /> -</org.chromium.chrome.browser.suggestions.SuggestionsTileView> +</org.chromium.chrome.browser.suggestions.tile.SuggestionsTileView>
diff --git a/chrome/android/java/res/layout/top_sites_tile_view.xml b/chrome/android/java/res/layout/top_sites_tile_view.xml index aca78aa7..1b683063 100644 --- a/chrome/android/java/res/layout/top_sites_tile_view.xml +++ b/chrome/android/java/res/layout/top_sites_tile_view.xml
@@ -7,7 +7,6 @@ <org.chromium.chrome.browser.suggestions.tile.TopSitesTileView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" android:layout_width="@dimen/tile_view_width" android:layout_height="wrap_content" android:paddingStart="@dimen/tile_view_padding" @@ -20,7 +19,7 @@ android:layout_height="@dimen/tile_view_icon_size" android:layout_gravity="center_horizontal" android:layout_marginTop="@dimen/tile_view_icon_background_margin_top_modern" - android:background="@drawable/tile_view_icon_background_modern" /> + android:background="@drawable/tile_view_hairline_border_background" /> <!-- The main icon. --> <ImageView
diff --git a/chrome/android/java/res/layout/top_sites_tile_view_condensed.xml b/chrome/android/java/res/layout/top_sites_tile_view_condensed.xml index 1ca4fe9..ff97c3e 100644 --- a/chrome/android/java/res/layout/top_sites_tile_view_condensed.xml +++ b/chrome/android/java/res/layout/top_sites_tile_view_condensed.xml
@@ -7,7 +7,6 @@ <org.chromium.chrome.browser.suggestions.tile.TopSitesTileView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" android:layout_width="@dimen/tile_view_width_condensed" android:layout_height="wrap_content" android:paddingStart="@dimen/tile_view_padding" @@ -20,7 +19,7 @@ android:layout_height="@dimen/tile_view_icon_size" android:layout_gravity="center_horizontal" android:layout_marginTop="@dimen/tile_view_icon_background_margin_top_modern" - android:background="@drawable/tile_view_icon_background_modern" /> + android:background="@drawable/tile_view_hairline_border_background" /> <!-- The main icon. --> <ImageView
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml index 13c4232..6892ee4 100644 --- a/chrome/android/java/res/values/dimens.xml +++ b/chrome/android/java/res/values/dimens.xml
@@ -313,7 +313,6 @@ <!-- NTP dimensions --> <dimen name="tile_grid_layout_max_width">504dp</dimen> <dimen name="tile_grid_layout_padding_top">24dp</dimen> - <dimen name="tile_grid_layout_padding_start">12dp</dimen> <dimen name="tile_grid_layout_no_logo_padding_top">20dp</dimen> <dimen name="tile_grid_layout_bleed">8dp</dimen> <dimen name="tile_grid_layout_vertical_spacing">6dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java index 819d6108..f1c72253 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
@@ -39,6 +39,7 @@ import org.chromium.base.task.AsyncTask; import org.chromium.blink_public.platform.WebDisplayMode; import org.chromium.chrome.R; +import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.chrome.browser.webapps.WebApkInfo; import org.chromium.chrome.browser.webapps.WebappActivity; import org.chromium.chrome.browser.webapps.WebappAuthenticator; @@ -274,7 +275,12 @@ */ private static void showAddedToHomescreenToast(final String title) { Context applicationContext = ContextUtils.getApplicationContext(); - String toastText = applicationContext.getString(R.string.added_to_homescreen, title); + String toastText; + if (FeatureUtilities.isNoTouchModeEnabled()) { + toastText = applicationContext.getString(R.string.added_to_apps, title); + } else { + toastText = applicationContext.getString(R.string.added_to_homescreen, title); + } showToast(toastText); } @@ -701,7 +707,8 @@ private static boolean isRequestPinShortcutSupported() { if (!sCheckedIfRequestPinShortcutSupported) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && !FeatureUtilities.isNoTouchModeEnabled()) { checkIfRequestPinShortcutSupported(); } sCheckedIfRequestPinShortcutSupported = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java index 0eec83e4..51f956f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
@@ -20,7 +20,7 @@ import org.chromium.chrome.browser.native_page.ContextMenuManager; import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate; import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.suggestions.TileGridLayout; +import org.chromium.chrome.browser.suggestions.tile.TileGridLayout; import org.chromium.chrome.browser.widget.RoundedIconGenerator; import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.ui.base.PageTransition;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java index d29deb4..cd9155f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesSection.java
@@ -23,7 +23,7 @@ import org.chromium.chrome.browser.ntp.NewTabPageUma; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle; -import org.chromium.chrome.browser.suggestions.TileGridLayout; +import org.chromium.chrome.browser.suggestions.tile.TileGridLayout; import org.chromium.chrome.browser.util.ViewUtils; import org.chromium.components.feature_engagement.Tracker; import org.chromium.content_public.browser.LoadUrlParams;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java index 239af73..1d16e4e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
@@ -178,6 +178,8 @@ String template = getContext().getString(R.string.duplicate_download_request_infobar_text); model.set(ModalDialogProperties.TITLE, + getContext().getResources().getString(R.string.menu_download)); + model.set(ModalDialogProperties.MESSAGE, getDownloadMessageText(getContext(), template).toString()); return model;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java index 7c25482..9ec9648 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -47,9 +47,9 @@ import org.chromium.chrome.browser.suggestions.SuggestionsMetrics; import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate; import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl; -import org.chromium.chrome.browser.suggestions.Tile; -import org.chromium.chrome.browser.suggestions.TileGroup; -import org.chromium.chrome.browser.suggestions.TileGroupDelegateImpl; +import org.chromium.chrome.browser.suggestions.tile.Tile; +import org.chromium.chrome.browser.suggestions.tile.TileGroup; +import org.chromium.chrome.browser.suggestions.tile.TileGroupDelegateImpl; import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab.TabHidingType;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java index 25f0a8f..a854524 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
@@ -41,15 +41,15 @@ import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; import org.chromium.chrome.browser.partnercustomizations.HomepageManager; import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.suggestions.SiteSection; -import org.chromium.chrome.browser.suggestions.SiteSectionViewHolder; import org.chromium.chrome.browser.suggestions.SiteSuggestion; import org.chromium.chrome.browser.suggestions.SuggestionsConfig; import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory; -import org.chromium.chrome.browser.suggestions.Tile; -import org.chromium.chrome.browser.suggestions.TileGridLayout; -import org.chromium.chrome.browser.suggestions.TileGroup; -import org.chromium.chrome.browser.suggestions.TileRenderer; +import org.chromium.chrome.browser.suggestions.tile.SiteSection; +import org.chromium.chrome.browser.suggestions.tile.SiteSectionViewHolder; +import org.chromium.chrome.browser.suggestions.tile.Tile; +import org.chromium.chrome.browser.suggestions.tile.TileGridLayout; +import org.chromium.chrome.browser.suggestions.tile.TileGroup; +import org.chromium.chrome.browser.suggestions.tile.TileRenderer; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.chrome.browser.util.MathUtils;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java index f258c4b..ed6823c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -27,7 +27,7 @@ import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory; import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate; -import org.chromium.chrome.browser.suggestions.TileGroup; +import org.chromium.chrome.browser.suggestions.tile.TileGroup; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.util.ViewUtils; import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java index e294900..3c8efd2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -73,8 +73,6 @@ public class LocationBarLayout extends FrameLayout implements OnClickListener, LocationBar, AutocompleteDelegate, FakeboxDelegate, LocationBarVoiceRecognitionHandler.Delegate { - private static final String TAG = "cr_LocationBar"; - protected ImageButton mDeleteButton; protected ImageButton mMicButton; protected View mUrlBar; @@ -106,7 +104,6 @@ private OmniboxPrerender mOmniboxPrerender; - private boolean mOmniboxVoiceSearchAlwaysVisible; protected float mUrlFocusChangePercent; protected LinearLayout mUrlActionContainer; @@ -403,9 +400,7 @@ mUrlFocusChangeInProgress = inProgress; if (!inProgress) { updateButtonVisibility(); - } - if (!inProgress) { // The accessibility bounding box is not properly updated when focusing the Omnibox // from the NTP fakebox. Clearing/re-requesting focus triggers the bounding box to // be recalculated.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java index 6d9e539a..27c98a07 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
@@ -311,9 +311,7 @@ public void addAcceptedPaymentMethodIfRecognized(PaymentMethodData data) { assert data != null; String method = data.supportedMethod; - if (mCardIssuerNetworks.containsKey(method)) { - addAcceptedNetwork(method); - } else if (BasicCardUtils.BASIC_CARD_METHOD_NAME.equals(method)) { + if (BasicCardUtils.BASIC_CARD_METHOD_NAME.equals(method)) { Set<String> basicCardNetworks = BasicCardUtils.convertBasicCardToNetworks(data); mAcceptedBasicCardIssuerNetworks.addAll(basicCardNetworks); for (String network : basicCardNetworks) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java deleted file mode 100644 index 566c7c4..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java +++ /dev/null
@@ -1,92 +0,0 @@ -// 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.chrome.browser.suggestions; - -import android.support.annotation.Nullable; - -import org.chromium.base.annotations.CalledByNative; - -import java.util.List; - -/** - * Methods to provide most recent urls, titles and thumbnails. - */ -public interface MostVisitedSites { - /** - * An interface for handling events in {@link MostVisitedSites}. - */ - interface Observer { - /** This is called when the list of most visited URLs is initially available or updated. */ - void onSiteSuggestionsAvailable(List<SiteSuggestion> siteSuggestions); - - /** - * This is called when a previously uncached icon has been fetched. - * Parameters guaranteed to be non-null. - * - * @param siteUrl URL of site with newly-cached icon. - */ - void onIconMadeAvailable(String siteUrl); - } - - /** - * An interface to provide {@link MostVisitedSites} with platform-specific home page data. - */ - interface HomepageClient { - /** - * @return True if homepage tile should be shown. - */ - @CalledByNative("HomepageClient") - boolean isHomepageTileEnabled(); - - /** - * @return The raw URL of the currently set home page. - */ - @CalledByNative("HomepageClient") - @Nullable - String getHomepageUrl(); - } - - /** - * This instance must not be used after calling destroy(). - */ - void destroy(); - - /** - * Sets the recipient for events from {@link MostVisitedSites}. The observer may be notified - * synchronously or asynchronously. - * @param observer The observer to be notified. - * @param numSites The maximum number of sites to return. - */ - void setObserver(Observer observer, int numSites); - - /** - * Blacklists a URL from the most visited URLs list. - */ - void addBlacklistedUrl(String url); - - /** - * Removes a URL from the most visited URLs blacklist. - */ - void removeBlacklistedUrl(String url); - - /** - * Records metrics about an impression of the surface with tiles. - * @param tilesCount Count of tiles available on the surface at the moment. - */ - void recordPageImpression(int tilesCount); - - /** - * Records metrics about an impression of a tile including its source (local, server, ...) and - * its visual type. - * @param tile Object holding the details of a tile. - */ - void recordTileImpression(Tile tile); - - /** - * Records the opening of a Most Visited Item. - * @param tile Object holding the details of a tile. - */ - void recordOpenedMostVisitedItem(Tile tile); -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java deleted file mode 100644 index b30a92a9..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java +++ /dev/null
@@ -1,195 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.suggestions; - -import android.text.TextUtils; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNIAdditionalImport; -import org.chromium.chrome.browser.ntp.NewTabPage; -import org.chromium.chrome.browser.partnercustomizations.HomepageManager; -import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.util.FeatureUtilities; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -/** - * Methods to bridge into native history to provide most recent urls, titles and thumbnails. - */ -@JNIAdditionalImport(MostVisitedSites.class) // Needed for the Observer usage in the native calls. -public class MostVisitedSitesBridge - implements MostVisitedSites, HomepageManager.HomepageStateListener { - /** - * Maximum number of tiles that is explicitly supported. UMA relies on this value, so even if - * the UI supports it, getting more can raise unexpected issues. - */ - public static final int MAX_TILE_COUNT = 12; - - private long mNativeMostVisitedSitesBridge; - - private MostVisitedSites.Observer mWrappedObserver; - - /** - * MostVisitedSites constructor requires a valid user profile object. - * - * @param profile The profile for which to fetch most visited sites. - */ - public MostVisitedSitesBridge(Profile profile) { - mNativeMostVisitedSitesBridge = nativeInit(profile); - // The first tile replaces is replaced with homepage tile if NTPButton is enabled. Setting - // a homepage client to provide Java side information. - if (FeatureUtilities.isNewTabPageButtonEnabled() - && FeatureUtilities.isHomepageTileEnabled()) { - nativeSetHomepageClient(mNativeMostVisitedSitesBridge, new HomepageClient() { - @Override - public boolean isHomepageTileEnabled() { - return HomepageManager.isHomepageEnabled() - && !NewTabPage.isNTPUrl(getHomepageUrl()) - && !TextUtils.isEmpty(HomepageManager.getHomepageUri()); - } - - @Override - public String getHomepageUrl() { - return HomepageManager.getHomepageUri(); - } - }); - HomepageManager.getInstance().addListener(this); - } - } - - /** - * Cleans up the C++ side of this class. This instance must not be used after calling destroy(). - */ - @Override - public void destroy() { - // Stop listening even if it was not started in the first place. (Handled without errors.) - HomepageManager.getInstance().removeListener(this); - assert mNativeMostVisitedSitesBridge != 0; - nativeDestroy(mNativeMostVisitedSitesBridge); - mNativeMostVisitedSitesBridge = 0; - } - - @Override - public void setObserver(Observer observer, int numSites) { - assert numSites <= MAX_TILE_COUNT; - mWrappedObserver = observer; - - nativeSetObserver(mNativeMostVisitedSitesBridge, this, numSites); - } - - @Override - public void addBlacklistedUrl(String url) { - nativeAddOrRemoveBlacklistedUrl(mNativeMostVisitedSitesBridge, url, true); - } - - @Override - public void removeBlacklistedUrl(String url) { - nativeAddOrRemoveBlacklistedUrl(mNativeMostVisitedSitesBridge, url, false); - } - - @Override - public void recordPageImpression(int tilesCount) { - nativeRecordPageImpression(mNativeMostVisitedSitesBridge, tilesCount); - } - - @Override - public void recordTileImpression(Tile tile) { - nativeRecordTileImpression(mNativeMostVisitedSitesBridge, tile.getIndex(), tile.getType(), - tile.getIconType(), tile.getTitleSource(), tile.getSource(), - tile.getData().dataGenerationTime.getTime(), tile.getUrl()); - } - - @Override - public void recordOpenedMostVisitedItem(Tile tile) { - nativeRecordOpenedMostVisitedItem(mNativeMostVisitedSitesBridge, tile.getIndex(), - tile.getType(), tile.getTitleSource(), tile.getSource(), - tile.getData().dataGenerationTime.getTime()); - } - - @Override - public void onHomepageStateUpdated() { - assert mNativeMostVisitedSitesBridge != 0; - // Ensure even a blacklisted homepage can be set as tile when (re-)enabling it. - if (HomepageManager.isHomepageEnabled()) { - removeBlacklistedUrl(HomepageManager.getHomepageUri()); - } - nativeOnHomepageStateChanged(mNativeMostVisitedSitesBridge); - } - - /** - * Utility function to convert JNI friendly site suggestion data to a Java friendly list of - * {@link SiteSuggestion}s. - */ - public static List<SiteSuggestion> buildSiteSuggestions(String[] titles, String[] urls, - int[] sections, String[] whitelistIconPaths, int[] titleSources, int[] sources, - long[] dataGenerationTimesMs) { - List<SiteSuggestion> siteSuggestions = new ArrayList<>(titles.length); - for (int i = 0; i < titles.length; ++i) { - siteSuggestions.add(new SiteSuggestion(titles[i], urls[i], whitelistIconPaths[i], - titleSources[i], sources[i], sections[i], new Date(dataGenerationTimesMs[i]))); - } - return siteSuggestions; - } - - /** - * This is called when the list of most visited URLs is initially available or updated. - * Parameters guaranteed to be non-null. - * - * @param titles Array of most visited url page titles. - * @param urls Array of most visited URLs, including popular URLs if - * available and necessary (i.e. there aren't enough most - * visited URLs). - * @param whitelistIconPaths The paths to the icon image files for whitelisted tiles, empty - * strings otherwise. - * @param sources For each tile, the {@code TileSource} that generated the tile. - */ - @CalledByNative - private void onURLsAvailable(String[] titles, String[] urls, int[] sections, - String[] whitelistIconPaths, int[] titleSources, int[] sources, - long[] dataGenerationTimesMs) { - // Don't notify observer if we've already been destroyed. - if (mNativeMostVisitedSitesBridge == 0) return; - - List<SiteSuggestion> suggestions = new ArrayList<>(); - - suggestions.addAll(buildSiteSuggestions(titles, urls, sections, whitelistIconPaths, - titleSources, sources, dataGenerationTimesMs)); - - mWrappedObserver.onSiteSuggestionsAvailable(suggestions); - } - - /** - * This is called when a previously uncached icon has been fetched. - * Parameters guaranteed to be non-null. - * - * @param siteUrl URL of site with newly-cached icon. - */ - @CalledByNative - private void onIconMadeAvailable(String siteUrl) { - // Don't notify observer if we've already been destroyed. - if (mNativeMostVisitedSitesBridge != 0) { - mWrappedObserver.onIconMadeAvailable(siteUrl); - } - } - - private native long nativeInit(Profile profile); - private native void nativeDestroy(long nativeMostVisitedSitesBridge); - private native void nativeOnHomepageStateChanged(long nativeMostVisitedSitesBridge); - private native void nativeSetHomepageClient( - long nativeMostVisitedSitesBridge, MostVisitedSites.HomepageClient homePageClient); - private native void nativeSetObserver( - long nativeMostVisitedSitesBridge, MostVisitedSitesBridge observer, int numSites); - private native void nativeAddOrRemoveBlacklistedUrl( - long nativeMostVisitedSitesBridge, String url, boolean addUrl); - private native void nativeRecordPageImpression( - long nativeMostVisitedSitesBridge, int tilesCount); - private native void nativeRecordTileImpression(long nativeMostVisitedSitesBridge, int index, - int type, int iconType, int titleSource, int source, long dataGenerationTimeMs, - String url); - private native void nativeRecordOpenedMostVisitedItem(long nativeMostVisitedSitesBridge, - int index, int tileType, int titleSource, int source, long dataGenerationTimeMs); -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSection.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSection.java deleted file mode 100644 index bc5b66e..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSection.java +++ /dev/null
@@ -1,123 +0,0 @@ -// 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.chrome.browser.suggestions; - -import android.support.annotation.LayoutRes; -import android.view.LayoutInflater; -import android.view.ViewGroup; - -import org.chromium.base.ContextUtils; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.ChromeFeatureList; -import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge; -import org.chromium.chrome.browser.native_page.ContextMenuManager; -import org.chromium.chrome.browser.ntp.cards.ItemViewType; -import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder; -import org.chromium.chrome.browser.ntp.cards.OptionalLeaf; -import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; -import org.chromium.chrome.browser.widget.displaystyle.UiConfig; - -/** - * The model and controller for a group of site suggestions. - * @deprecated This class is still being used, but not in the New Tab Page RecyclerView - * anymore. It still uses the latter's base classes until SiteSection is migrated to the new - * UI architecture. - */ -@Deprecated -public class SiteSection extends OptionalLeaf implements TileGroup.Observer { - /** - * The maximum number of tiles to try and fit in a row. On smaller screens, there may not be - * enough space to fit all of them. - */ - private static final int MAX_TILE_COLUMNS = 4; - private static final int TILE_TITLE_LINES = 1; - - private final TileGroup mTileGroup; - private final TileRenderer mTileRenderer; - - public static ViewGroup inflateSiteSection(ViewGroup parent) { - return (ViewGroup) LayoutInflater.from(parent.getContext()) - .inflate(getLayout(), parent, false); - } - - public static SiteSectionViewHolder createViewHolder(ViewGroup view, UiConfig uiConfig) { - return new TileGridViewHolder(view, getMaxTileRows(), MAX_TILE_COLUMNS, uiConfig); - } - - public SiteSection(SuggestionsUiDelegate uiDelegate, ContextMenuManager contextMenuManager, - TileGroup.Delegate tileGroupDelegate, OfflinePageBridge offlinePageBridge, - UiConfig uiConfig) { - mTileRenderer = new TileRenderer(ContextUtils.getApplicationContext(), - SuggestionsConfig.getTileStyle(uiConfig), TILE_TITLE_LINES, - uiDelegate.getImageFetcher()); - mTileGroup = new TileGroup(mTileRenderer, uiDelegate, contextMenuManager, tileGroupDelegate, - /* observer = */ this, offlinePageBridge); - mTileGroup.startObserving(MAX_TILE_COLUMNS * getMaxTileRows()); - } - - @Override - @ItemViewType - protected int getItemViewType() { - // Throw an exception instead of just `assert false` to avoid compiler warnings about the - // return value. - throw new IllegalStateException(); - } - - @Override - protected void onBindViewHolder(NewTabPageViewHolder holder) { - SiteSectionViewHolder siteSectionView = (SiteSectionViewHolder) holder; - siteSectionView.bindDataSource(mTileGroup, mTileRenderer); - siteSectionView.refreshData(); - } - - @Override - public String describeForTesting() { - // Throw an exception instead of just `assert false` to avoid compiler warnings about the - // return value. - throw new IllegalStateException(); - } - - @Override - public void onTileDataChanged() { - setVisibilityInternal(!mTileGroup.isEmpty()); - if (!isVisible()) return; - notifyItemChanged(0, (holder) -> ((SiteSectionViewHolder) holder).refreshData()); - } - - @Override - public void onTileCountChanged() { - onTileDataChanged(); - } - - @Override - public void onTileIconChanged(Tile tile) { - if (!isVisible()) return; - notifyItemChanged(0, (holder) -> ((SiteSectionViewHolder) holder).updateIconView(tile)); - } - - @Override - public void onTileOfflineBadgeVisibilityChanged(Tile tile) { - if (!isVisible()) return; - notifyItemChanged(0, (holder) -> ((SiteSectionViewHolder) holder).updateOfflineBadge(tile)); - } - - TileGroup getTileGroupForTesting() { - return mTileGroup; - } - - private static int getMaxTileRows() { - if (ChromeFeatureList.isEnabled(ChromeFeatureList.EXPLORE_SITES) - && !ExploreSitesBridge.isIntegratedWithMostLikely( - ExploreSitesBridge.getVariation())) { - return 1; - } - return 2; - } - - @LayoutRes - private static int getLayout() { - return R.layout.suggestions_site_tile_grid_modern; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSectionViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSectionViewHolder.java deleted file mode 100644 index 25b585a..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSectionViewHolder.java +++ /dev/null
@@ -1,60 +0,0 @@ -// 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.chrome.browser.suggestions; - -import android.support.annotation.CallSuper; -import android.support.annotation.Nullable; -import android.view.View; - -import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder; - -/** - * Describes a portion of UI responsible for rendering a group of sites. It abstracts general tasks - * related to initialising and updating this UI. - */ -public abstract class SiteSectionViewHolder extends NewTabPageViewHolder { - protected TileGroup mTileGroup; - protected TileRenderer mTileRenderer; - - /** - * Constructs a {@link SiteSectionViewHolder} used to display tiles in both NTP and Chrome Home. - * - * @param itemView The {@link View} for this item - */ - public SiteSectionViewHolder(View itemView) { - super(itemView); - } - - /** Initialise the view, letting it know the data it will have to display. */ - @CallSuper - public void bindDataSource(TileGroup tileGroup, TileRenderer tileRenderer) { - mTileGroup = tileGroup; - mTileRenderer = tileRenderer; - } - - /** - * Sets a new icon on the child view with a matching site. - * @param tile The tile that holds the data to populate the tile view. - */ - public void updateIconView(Tile tile) { - SuggestionsTileView tileView = findTileView(tile.getData()); - if (tileView != null) tileView.renderIcon(tile); - } - - /** - * Updates the visibility of the offline badge on the child view with a matching site. - * @param tile The tile that holds the data to populate the tile view. - */ - public void updateOfflineBadge(Tile tile) { - SuggestionsTileView tileView = findTileView(tile.getData()); - if (tileView != null) tileView.renderOfflineBadge(tile); - } - - /** Clears the current data and displays the current state of the model. */ - public abstract void refreshData(); - - @Nullable - public abstract SuggestionsTileView findTileView(SiteSuggestion data); -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSuggestion.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSuggestion.java index d16a5307..1826df1d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSuggestion.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SiteSuggestion.java
@@ -4,6 +4,10 @@ package org.chromium.chrome.browser.suggestions; +import org.chromium.chrome.browser.suggestions.tile.TileSectionType; +import org.chromium.chrome.browser.suggestions.tile.TileSource; +import org.chromium.chrome.browser.suggestions.tile.TileTitleSource; + import java.util.Date; /** @@ -27,7 +31,10 @@ @TileSource public final int source; - /** The {@link TileSectionType} the tile is contained in. */ + /** + * The {@link org.chromium.chrome.browser.suggestions.tile.TileSectionType} the tile is + * contained in. + */ @TileSectionType public final int sectionType;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java index 1aec6f0..20414d5c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsDependencyFactory.java
@@ -17,6 +17,8 @@ import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites; +import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSitesBridge; import org.chromium.chrome.browser.widget.ThumbnailProvider; import org.chromium.chrome.browser.widget.ThumbnailProviderImpl;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java index 78b7274..fd06fcf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsMetrics.java
@@ -16,6 +16,7 @@ import org.chromium.chrome.browser.preferences.ChromePreferenceManager; import org.chromium.chrome.browser.preferences.Pref; import org.chromium.chrome.browser.preferences.PrefServiceBridge; +import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSitesBridge; import org.chromium.chrome.browser.tab.Tab; /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsTileView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsTileView.java deleted file mode 100644 index b20a7d91..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsTileView.java +++ /dev/null
@@ -1,80 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.suggestions; - -import android.content.Context; -import android.content.res.Resources; -import android.util.AttributeSet; - -import org.chromium.chrome.R; -import org.chromium.chrome.browser.ntp.TitleUtil; -import org.chromium.chrome.browser.widget.tile.TileWithTextView; - -/** - * The view for a site suggestion tile. Displays the title of the site beneath a large icon. If a - * large icon isn't available, displays a rounded rectangle with a single letter in its place. - */ -public class SuggestionsTileView extends TileWithTextView { - /** The data currently associated to this tile. */ - private SiteSuggestion mData; - - /** - * Constructor for inflating from XML. - */ - public SuggestionsTileView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - /** - * Initializes the view using the data held by {@code tile}. This should be called immediately - * after inflation. - * @param tile The tile that holds the data to populate this view. - * @param titleLines The number of text lines to use for the tile title. - */ - public void initialize(Tile tile, int titleLines) { - super.initialize(TitleUtil.getTitleForDisplay(tile.getTitle(), tile.getUrl()), - tile.isOfflineAvailable(), tile.getIcon(), titleLines); - mData = tile.getData(); - setIconViewLayoutParams(tile); - } - - /** Retrieves data associated with this view. */ - public SiteSuggestion getData() { - return mData; - } - - /** Retrieves url associated with this view. */ - public String getUrl() { - return mData.url; - } - - /** Renders icon based on tile data. */ - public void renderIcon(Tile tile) { - setIconDrawable(tile.getIcon()); - setIconViewLayoutParams(tile); - } - - public void renderOfflineBadge(Tile tile) { - setOfflineBadgeVisibility(tile.isOfflineAvailable()); - } - - protected void setIconViewLayoutParams(Tile tile) { - MarginLayoutParams params = (MarginLayoutParams) mIconView.getLayoutParams(); - Resources resources = getResources(); - if (tile.getType() == TileVisualType.ICON_COLOR - || tile.getType() == TileVisualType.ICON_DEFAULT) { - params.width = resources.getDimensionPixelSize(R.dimen.tile_view_monogram_size_modern); - params.height = resources.getDimensionPixelSize(R.dimen.tile_view_monogram_size_modern); - params.topMargin = - resources.getDimensionPixelSize(R.dimen.tile_view_monogram_margin_top_modern); - } else { - params.width = resources.getDimensionPixelSize(R.dimen.tile_view_icon_size_modern); - params.height = resources.getDimensionPixelSize(R.dimen.tile_view_icon_size_modern); - params.topMargin = - resources.getDimensionPixelSize(R.dimen.tile_view_icon_margin_top_modern); - } - mIconView.setLayoutParams(params); - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/Tile.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/Tile.java deleted file mode 100644 index c7a3ef5f..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/Tile.java +++ /dev/null
@@ -1,155 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.suggestions; - -import android.graphics.drawable.Drawable; -import android.support.annotation.Nullable; - -import org.chromium.chrome.browser.favicon.IconType; - -/** - * Holds the details to populate a site suggestion tile. - */ -public class Tile implements OfflinableSuggestion { - private final SiteSuggestion mSiteData; - - private final int mIndex; - - @TileVisualType - private int mType = TileVisualType.NONE; - - @IconType - private int mIconType = IconType.INVALID; - - @Nullable - private Drawable mIcon; - - @Nullable - private Long mOfflinePageOfflineId; - - /** - * @param suggestion The site data we want to populate the tile with. - * @param index The index of this tile in the list of tiles. - */ - public Tile(SiteSuggestion suggestion, int index) { - mSiteData = suggestion; - mIndex = index; - } - - public SiteSuggestion getData() { - return mSiteData; - } - - @Override - public String getUrl() { - return mSiteData.url; - } - - @Override - public void setOfflinePageOfflineId(@Nullable Long offlineId) { - mOfflinePageOfflineId = offlineId; - } - - @Nullable - @Override - public Long getOfflinePageOfflineId() { - return mOfflinePageOfflineId; - } - - @Override - public boolean requiresExactOfflinePage() { - return false; - } - - /** - * @return The title of this tile. - */ - public String getTitle() { - return mSiteData.title; - } - - /** - * @return Whether this tile is available offline. - */ - public boolean isOfflineAvailable() { - return getOfflinePageOfflineId() != null; - } - - /** - * @return The index of this tile in the list of tiles. - */ - public int getIndex() { - return mIndex; - } - - /** - * @return The source of this tile's title. Used for metrics tracking. Valid values are listed - * in {@code TileTitleSource}. - */ - @TileTitleSource - public int getTitleSource() { - return mSiteData.titleSource; - } - - /** - * @return The source of this tile. Used for metrics tracking. Valid values are listed in - * {@code TileSource}. - */ - @TileSource - public int getSource() { - return mSiteData.source; - } - - /** - * @return The visual type of this tile. Valid values are listed in {@link TileVisualType}. - */ - @TileVisualType - public int getType() { - return mType; - } - - /** - * Sets the visual type of this tile. Valid values are listed in - * {@link TileVisualType}. - */ - public void setType(@TileVisualType int type) { - mType = type; - } - - /** - * @return The icon type of this tile. Valid values are listed in {@link IconType}. - */ - @IconType - public int getIconType() { - return mIconType; - } - - /** - * Sets the icon type of this tile. Valid values are listed in {@link IconType}. - */ - public void setIconType(@IconType int iconType) { - mIconType = iconType; - } - - /** - * @return The icon, may be null. - */ - @Nullable - public Drawable getIcon() { - return mIcon; - } - - /** - * Updates the icon drawable. - */ - public void setIcon(@Nullable Drawable icon) { - mIcon = icon; - } - - @TileSectionType - public int getSectionType() { - return mSiteData.sectionType; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGridLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGridLayout.java deleted file mode 100644 index c9cd11e..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGridLayout.java +++ /dev/null
@@ -1,175 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.suggestions; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.support.annotation.Nullable; -import android.util.AttributeSet; -import android.util.Pair; -import android.view.View; -import android.widget.FrameLayout; - -import org.chromium.base.VisibleForTesting; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.util.MathUtils; - -/** - * A layout that arranges tiles in a grid. - */ -public class TileGridLayout extends FrameLayout { - private final int mVerticalSpacing; - private final int mMinHorizontalSpacing; - private final int mMaxHorizontalSpacing; - private final int mMaxWidth; - - private int mMaxRows; - private int mMaxColumns; - - /** - * Constructor for inflating from XML. - * - * @param context The view context in which this item will be shown. - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public TileGridLayout(Context context, AttributeSet attrs) { - super(context, attrs); - - Resources res = getResources(); - mVerticalSpacing = res.getDimensionPixelOffset(R.dimen.tile_grid_layout_vertical_spacing); - TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.TileGridLayout); - mMinHorizontalSpacing = styledAttrs.getDimensionPixelOffset( - R.styleable.TileGridLayout_minHorizontalSpacing, - res.getDimensionPixelOffset(R.dimen.tile_grid_layout_min_horizontal_spacing)); - styledAttrs.recycle(); - mMaxHorizontalSpacing = Integer.MAX_VALUE; - mMaxWidth = Integer.MAX_VALUE; - } - - /** - * Sets the maximum number of rows to display. Any items that don't fit will be hidden. - */ - public void setMaxRows(int rows) { - mMaxRows = rows; - } - - /** - * Sets the maximum number of columns to display. Any items that don't fit will be hidden. - */ - public void setMaxColumns(int columns) { - mMaxColumns = columns; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int totalWidth = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth); - int childCount = getChildCount(); - if (childCount == 0) { - setMeasuredDimension(totalWidth, resolveSize(0, heightMeasureSpec)); - return; - } - - // Measure the children. We don't use the ViewGroup.measureChildren() method here because - // it only measures visible children. In a situation where a child is invisible before - // this measurement and we decide to show it after the measurement, it will not have its - // dimensions and will not be displayed. - for (int i = 0; i < childCount; i++) { - measureChild(getChildAt(i), MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); - } - - // Determine the number of columns that will fit. - int childHeight = getChildAt(0).getMeasuredHeight(); - int childWidth = getChildAt(0).getMeasuredWidth(); - int numColumns = MathUtils.clamp( - (totalWidth + mMinHorizontalSpacing) / (childWidth + mMinHorizontalSpacing), 1, - mMaxColumns); - - // Determine how much padding to use between and around the tiles. - int gridWidthMinusColumns = Math.max(0, totalWidth - numColumns * childWidth); - Pair<Integer, Integer> gridProperties = - computeHorizontalDimensions(true, gridWidthMinusColumns, numColumns); - int gridStart = gridProperties.first; - int horizontalSpacing = gridProperties.second; - - // Limit the number of rows to mMaxRows. - int visibleChildCount = Math.min(childCount, mMaxRows * numColumns); - - // Arrange the visible children in a grid. - int numRows = (visibleChildCount + numColumns - 1) / numColumns; - int paddingTop = getPaddingTop(); - boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; - - for (int i = 0; i < visibleChildCount; i++) { - View child = getChildAt(i); - child.setVisibility(View.VISIBLE); - int row = i / numColumns; - int column = i % numColumns; - int childTop = row * (childHeight + mVerticalSpacing); - int childStart = gridStart + (column * (childWidth + horizontalSpacing)); - MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams(); - layoutParams.setMargins(isRtl ? 0 : childStart, childTop, isRtl ? childStart : 0, 0); - child.setLayoutParams(layoutParams); - } - - // Hide any extra children in case there are more than needed for the maximum number of - // rows. - for (int i = visibleChildCount; i < childCount; i++) { - getChildAt(i).setVisibility(View.GONE); - } - - int totalHeight = paddingTop + getPaddingBottom() + numRows * childHeight - + (numRows - 1) * mVerticalSpacing; - - setMeasuredDimension(totalWidth, resolveSize(totalHeight, heightMeasureSpec)); - } - - /** - * @param spreadTiles Whether to spread the tiles with the same space between and around them. - * @param availableWidth The space available to spread between and around the tiles. - * @param numColumns The number of columns to be organised. - * @return The [gridStart, horizontalSpacing] pair of dimensions. - */ - @VisibleForTesting - Pair<Integer, Integer> computeHorizontalDimensions( - boolean spreadTiles, int availableWidth, int numColumns) { - int gridStart; - float horizontalSpacing; - if (spreadTiles) { - // Identically sized spacers are added both between and around the tiles. - int spacerCount = numColumns + 1; - horizontalSpacing = (float) availableWidth / spacerCount; - gridStart = Math.round(horizontalSpacing); - if (horizontalSpacing < mMinHorizontalSpacing) { - return computeHorizontalDimensions(false, availableWidth, numColumns); - } - } else { - // Ensure column spacing isn't greater than mMaxHorizontalSpacing. - long gridSidePadding = availableWidth - (long) mMaxHorizontalSpacing * (numColumns - 1); - if (gridSidePadding > 0) { - horizontalSpacing = mMaxHorizontalSpacing; - gridStart = (int) (gridSidePadding / 2); - } else { - horizontalSpacing = (float) availableWidth / Math.max(1, numColumns - 1); - gridStart = 0; - } - } - - assert horizontalSpacing >= mMinHorizontalSpacing; - assert horizontalSpacing <= mMaxHorizontalSpacing; - - return Pair.create(gridStart, Math.round(horizontalSpacing)); - } - - @Nullable - public SuggestionsTileView getTileView(SiteSuggestion suggestion) { - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - SuggestionsTileView tileView = (SuggestionsTileView) getChildAt(i); - if (suggestion.equals(tileView.getData())) return tileView; - } - return null; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGridViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGridViewHolder.java deleted file mode 100644 index 38ec806b..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGridViewHolder.java +++ /dev/null
@@ -1,59 +0,0 @@ -// 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.chrome.browser.suggestions; - -import android.content.res.Resources; -import android.view.ViewGroup; - -import org.chromium.chrome.R; -import org.chromium.chrome.browser.widget.displaystyle.UiConfig; - -import java.util.List; -/** - * A {@link SiteSectionViewHolder} specialised in displaying sites as a simple grid of tiles, - * through - * {@link TileGridLayout}. - */ -public class TileGridViewHolder extends SiteSectionViewHolder { - private final TileGridLayout mSectionView; - - public TileGridViewHolder(ViewGroup view, int maxRows, int maxColumns, UiConfig uiConfig) { - super(view); - - mSectionView = (TileGridLayout) itemView; - mSectionView.setMaxRows(maxRows); - mSectionView.setMaxColumns(maxColumns); - - Resources res = itemView.getResources(); - int defaultLateralMargin = - res.getDimensionPixelSize(R.dimen.tile_grid_layout_padding_start); - int wideLateralMargin = res.getDimensionPixelSize(R.dimen.ntp_wide_card_lateral_margins); - } - - @Override - public void refreshData() { - assert mTileGroup.getTileSections().size() == 1; - List<Tile> tiles = mTileGroup.getTileSections().get(TileSectionType.PERSONALIZED); - assert tiles != null; - - mTileRenderer.renderTileSection(tiles, mSectionView, mTileGroup.getTileSetupDelegate()); - mTileGroup.notifyTilesRendered(); - } - - @Override - public SuggestionsTileView findTileView(SiteSuggestion data) { - return mSectionView.getTileView(data); - } - - @Override - public void bindDataSource(TileGroup tileGroup, TileRenderer tileRenderer) { - super.bindDataSource(tileGroup, tileRenderer); - } - - @Override - public void recycle() { - super.recycle(); - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroup.java deleted file mode 100644 index 28e66d8..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroup.java +++ /dev/null
@@ -1,606 +0,0 @@ -// 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.chrome.browser.suggestions; - -import android.graphics.Bitmap; -import android.support.annotation.IntDef; -import android.support.annotation.Nullable; -import android.util.SparseArray; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnCreateContextMenuListener; - -import org.chromium.base.Callback; -import org.chromium.base.VisibleForTesting; -import org.chromium.chrome.browser.favicon.IconType; -import org.chromium.chrome.browser.favicon.LargeIconBridge; -import org.chromium.chrome.browser.native_page.ContextMenuManager; -import org.chromium.chrome.browser.native_page.ContextMenuManager.ContextMenuItemId; -import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; -import org.chromium.chrome.browser.offlinepages.OfflinePageItem; -import org.chromium.ui.mojom.WindowOpenDisposition; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * The model and controller for a group of site suggestion tiles. - */ -public class TileGroup implements MostVisitedSites.Observer { - /** - * Performs work in other parts of the system that the {@link TileGroup} should not know about. - */ - public interface Delegate { - /** - * @param tile The tile corresponding to the most visited item to remove. - * @param removalUndoneCallback The callback to invoke if the removal is reverted. The - * callback's argument is the URL being restored. - */ - void removeMostVisitedItem(Tile tile, Callback<String> removalUndoneCallback); - - void openMostVisitedItem(int windowDisposition, Tile tile); - - /** - * Gets the list of most visited sites. - * @param observer The observer to be notified with the list of sites. - * @param maxResults The maximum number of sites to retrieve. - */ - void setMostVisitedSitesObserver(MostVisitedSites.Observer observer, int maxResults); - - /** - * Called when the tile group has completely finished loading (all views will be inflated - * and any dependent resources will have been loaded). - * @param tiles The tiles owned by the {@link TileGroup}. Used to record metrics. - */ - void onLoadingComplete(List<Tile> tiles); - - /** - * To be called before this instance is abandoned to the garbage collector so it can do any - * necessary cleanups. This instance must not be used after this method is called. - */ - void destroy(); - } - - /** - * An observer for events in the {@link TileGroup}. - */ - public interface Observer { - /** - * Called when the tile group is initialised and when any of the tile data has changed, - * such as an icon, url, or title. - */ - void onTileDataChanged(); - - /** - * Called when the number of tiles has changed. - */ - void onTileCountChanged(); - - /** - * Called when a tile icon has changed. - * @param tile The tile for which the icon has changed. - */ - void onTileIconChanged(Tile tile); - - /** - * Called when the visibility of a tile's offline badge has changed. - * @param tile The tile for which the visibility of the offline badge has changed. - */ - void onTileOfflineBadgeVisibilityChanged(Tile tile); - } - - /** - * A delegate to allow {@link TileRenderer} to setup behaviours for the newly created views - * associated to a Tile. - */ - public interface TileSetupDelegate { - /** - * Returns a delegate that will handle user interactions with the view created for the tile. - */ - TileInteractionDelegate createInteractionDelegate(Tile tile); - - /** - * Returns a callback to be invoked when the icon for the provided tile is loaded. It will - * be responsible for updating the tile data and triggering the visual refresh. - */ - LargeIconBridge.LargeIconCallback createIconLoadCallback(Tile tile); - } - - /** - * Constants used to track the current operations on the group and notify the {@link Delegate} - * when the expected sequence of potentially asynchronous operations is complete. - */ - @VisibleForTesting - @IntDef({TileTask.FETCH_DATA, TileTask.SCHEDULE_ICON_FETCH, TileTask.FETCH_ICON}) - @Retention(RetentionPolicy.SOURCE) - @interface TileTask { - /** - * An event that should result in new data being loaded happened. - * Can be an asynchronous task, spanning from when the {@link Observer} is registered to - * when the initial load completes. - */ - int FETCH_DATA = 1; - - /** - * New tile data has been loaded and we are expecting the related icons to be fetched. - * Can be an asynchronous task, as we rely on it being triggered by the embedder, some time - * after {@link Observer#onTileDataChanged()} is called. - */ - int SCHEDULE_ICON_FETCH = 2; - - /** - * The icon for a tile is being fetched. - * Asynchronous task, that is started for each icon that needs to be loaded. - */ - int FETCH_ICON = 3; - } - - private final SuggestionsUiDelegate mUiDelegate; - private final ContextMenuManager mContextMenuManager; - private final Delegate mTileGroupDelegate; - private final Observer mObserver; - private final TileRenderer mTileRenderer; - - /** - * Tracks the tasks currently in flight. - * - * We only care about which ones are pending, not their order, and we can have multiple tasks - * pending of the same type. Hence exposing the type as Collection rather than List or Set. - */ - private final Collection<Integer> mPendingTasks = new ArrayList<>(); - - /** Access point to offline related features. */ - private final OfflineModelObserver mOfflineModelObserver; - - /** - * Source of truth for the tile data. Avoid keeping a reference to a tile in long running - * callbacks, as it might be thrown out before it is called. Use URL or site data to look it up - * at the right time instead. - * @see #findTile(SiteSuggestion) - * @see #findTilesForUrl(String) - */ - private SparseArray<List<Tile>> mTileSections = createEmptyTileData(); - - /** Most recently received tile data that has not been displayed yet. */ - @Nullable - private List<SiteSuggestion> mPendingTiles; - - /** - * URL of the most recently removed tile. Used to identify when a tile removal is confirmed by - * the tile backend. - */ - @Nullable - private String mPendingRemovalUrl; - - /** - * URL of the most recently added tile. Used to identify when a given tile's insertion is - * confirmed by the tile backend. This is relevant when a previously existing tile is removed, - * then the user undoes the action and wants that tile back. - */ - @Nullable - private String mPendingInsertionUrl; - - private boolean mHasReceivedData; - - // TODO(dgn): Attempt to avoid cycling dependencies with TileRenderer. Is there a better way? - private final TileSetupDelegate mTileSetupDelegate = new TileSetupDelegate() { - @Override - public TileInteractionDelegate createInteractionDelegate(Tile tile) { - return new TileInteractionDelegate(tile.getData()); - } - - @Override - public LargeIconBridge.LargeIconCallback createIconLoadCallback(Tile tile) { - // TODO(dgn): We could save on fetches by avoiding a new one when there is one pending - // for the same URL, and applying the result to all matched URLs. - boolean trackLoad = - isLoadTracked() && tile.getSectionType() == TileSectionType.PERSONALIZED; - if (trackLoad) addTask(TileTask.FETCH_ICON); - return new LargeIconCallbackImpl(tile.getData(), trackLoad); - } - }; - - /** - * @param tileRenderer Used to render icons. - * @param uiDelegate Delegate used to interact with the rest of the system. - * @param contextMenuManager Used to handle context menu invocations on the tiles. - * @param tileGroupDelegate Used for interactions with the Most Visited backend. - * @param observer Will be notified of changes to the tile data. - * @param offlinePageBridge Used to update the offline badge of the tiles. - */ - public TileGroup(TileRenderer tileRenderer, SuggestionsUiDelegate uiDelegate, - ContextMenuManager contextMenuManager, Delegate tileGroupDelegate, Observer observer, - OfflinePageBridge offlinePageBridge) { - mUiDelegate = uiDelegate; - mContextMenuManager = contextMenuManager; - mTileGroupDelegate = tileGroupDelegate; - mObserver = observer; - mTileRenderer = tileRenderer; - mOfflineModelObserver = new OfflineModelObserver(offlinePageBridge); - mUiDelegate.addDestructionObserver(mOfflineModelObserver); - } - - @Override - public void onSiteSuggestionsAvailable(List<SiteSuggestion> siteSuggestions) { - // Only transforms the incoming tiles and stores them in a buffer for when we decide to - // refresh the tiles in the UI. - - boolean removalCompleted = mPendingRemovalUrl != null; - boolean insertionCompleted = mPendingInsertionUrl == null; - - mPendingTiles = new ArrayList<>(); - for (SiteSuggestion suggestion : siteSuggestions) { - mPendingTiles.add(suggestion); - - // Only tiles in the personal section can be modified. - if (suggestion.sectionType != TileSectionType.PERSONALIZED) continue; - if (suggestion.url.equals(mPendingRemovalUrl)) removalCompleted = false; - if (suggestion.url.equals(mPendingInsertionUrl)) insertionCompleted = true; - } - - boolean expectedChangeCompleted = false; - if (mPendingRemovalUrl != null && removalCompleted) { - mPendingRemovalUrl = null; - expectedChangeCompleted = true; - } - if (mPendingInsertionUrl != null && insertionCompleted) { - mPendingInsertionUrl = null; - expectedChangeCompleted = true; - } - - if (!mHasReceivedData || !mUiDelegate.isVisible() || expectedChangeCompleted) loadTiles(); - } - - @Override - public void onIconMadeAvailable(String siteUrl) { - for (Tile tile : findTilesForUrl(siteUrl)) { - mTileRenderer.updateIcon(tile.getData(), - new LargeIconCallbackImpl(tile.getData(), /* trackLoadTask = */ false)); - } - } - - /** - * Instructs this instance to start listening for data. The {@link TileGroup.Observer} may be - * called immediately if new data is received synchronously. - * @param maxResults The maximum number of sites to retrieve. - */ - public void startObserving(int maxResults) { - addTask(TileTask.FETCH_DATA); - mTileGroupDelegate.setMostVisitedSitesObserver(this, maxResults); - } - - /** - * Method to be called when a tile render has been triggered, to let the {@link TileGroup} - * update its internal task tracking status. - * @see Delegate#onLoadingComplete(List) - */ - public void notifyTilesRendered() { - // Icon fetch scheduling was done when building the tile views. - if (isLoadTracked()) removeTask(TileTask.SCHEDULE_ICON_FETCH); - } - - /** @return the sites currently loaded in the group, grouped by vertical. */ - public SparseArray<List<Tile>> getTileSections() { - return mTileSections; - } - - public boolean hasReceivedData() { - return mHasReceivedData; - } - - /** @return Whether the group has no sites to display. */ - public boolean isEmpty() { - for (int i = 0; i < mTileSections.size(); i++) { - if (!mTileSections.valueAt(i).isEmpty()) return false; - } - return true; - } - - /** - * To be called when the view displaying the tile group becomes visible. - * @param trackLoadTask whether the delegate should be notified that the load is completed - * through {@link Delegate#onLoadingComplete(List)}. - */ - public void onSwitchToForeground(boolean trackLoadTask) { - if (trackLoadTask) addTask(TileTask.FETCH_DATA); - if (mPendingTiles != null) loadTiles(); - if (trackLoadTask) removeTask(TileTask.FETCH_DATA); - } - - /** Loads tile data from {@link #mPendingTiles} and clears it afterwards. */ - private void loadTiles() { - assert mPendingTiles != null; - - boolean isInitialLoad = !mHasReceivedData; - mHasReceivedData = true; - - boolean dataChanged = isInitialLoad; - List<Tile> personalisedTiles = mTileSections.get(TileSectionType.PERSONALIZED); - int oldPersonalisedTilesCount = personalisedTiles == null ? 0 : personalisedTiles.size(); - - SparseArray<List<Tile>> newSites = createEmptyTileData(); - for (int i = 0; i < mPendingTiles.size(); ++i) { - SiteSuggestion suggestion = mPendingTiles.get(i); - Tile tile = findTile(suggestion); - if (tile == null) { - dataChanged = true; - tile = new Tile(suggestion, i); - } - - List<Tile> sectionTiles = newSites.get(suggestion.sectionType); - if (sectionTiles == null) { - sectionTiles = new ArrayList<>(); - newSites.append(suggestion.sectionType, sectionTiles); - } - - // This is not supposed to happen but does. See https://crbug.com/703628 - if (findTile(suggestion.url, sectionTiles) != null) continue; - - sectionTiles.add(tile); - } - - mTileSections = newSites; - mPendingTiles = null; - - // TODO(dgn): change these events, maybe introduce new ones or just change semantics? This - // will depend on the UI to be implemented and the desired refresh behaviour. - List<Tile> personalizedTiles = mTileSections.get(TileSectionType.PERSONALIZED); - int numberOfPersonalizedTiles = personalizedTiles == null ? 0 : personalizedTiles.size(); - boolean countChanged = - isInitialLoad || numberOfPersonalizedTiles != oldPersonalisedTilesCount; - dataChanged = dataChanged || countChanged; - - if (!dataChanged) return; - - mOfflineModelObserver.updateAllSuggestionsOfflineAvailability( - /* reportPrefetchedSuggestionsCount = */ false); - - if (countChanged) mObserver.onTileCountChanged(); - - if (isLoadTracked()) addTask(TileTask.SCHEDULE_ICON_FETCH); - mObserver.onTileDataChanged(); - - if (isInitialLoad) removeTask(TileTask.FETCH_DATA); - } - - @Nullable - private Tile findTile(SiteSuggestion suggestion) { - if (mTileSections.get(suggestion.sectionType) == null) return null; - for (Tile tile : mTileSections.get(suggestion.sectionType)) { - if (tile.getData().equals(suggestion)) return tile; - } - return null; - } - - /** - * @param url The URL to search for. - * @param tiles The section to search in, represented by the contained list of tiles. - * @return A tile matching the provided URL and section, or {@code null} if none is found. */ - private Tile findTile(String url, @Nullable List<Tile> tiles) { - if (tiles == null) return null; - for (Tile tile : tiles) { - if (tile.getUrl().equals(url)) return tile; - } - return null; - } - - /** @return All tiles matching the provided URL, or an empty list if none is found. */ - private List<Tile> findTilesForUrl(String url) { - List<Tile> tiles = new ArrayList<>(); - for (int i = 0; i < mTileSections.size(); ++i) { - for (Tile tile : mTileSections.valueAt(i)) { - if (tile.getUrl().equals(url)) tiles.add(tile); - } - } - return tiles; - } - - private void addTask(@TileTask int task) { - mPendingTasks.add(task); - } - - private void removeTask(@TileTask int task) { - boolean removedTask = mPendingTasks.remove(Integer.valueOf(task)); - assert removedTask; - - if (mPendingTasks.isEmpty()) { - // TODO(dgn): We only notify about the personal tiles because that's the only ones we - // wait for to be loaded. We also currently rely on the tile order in the returned - // array as the reported position in UMA, but this is not accurate and would be broken - // if we returned all the tiles regardless of sections. - List<Tile> personalTiles = mTileSections.get(TileSectionType.PERSONALIZED); - assert personalTiles != null; - mTileGroupDelegate.onLoadingComplete(personalTiles); - } - } - - /** - * @return Whether the current load is being tracked. Unrequested task tracking updates should - * not be sent, as it would cause calling {@link Delegate#onLoadingComplete(List)} at the - * wrong moment. - */ - private boolean isLoadTracked() { - return mPendingTasks.contains(TileTask.FETCH_DATA) - || mPendingTasks.contains(TileTask.SCHEDULE_ICON_FETCH); - } - - @VisibleForTesting - boolean isTaskPending(@TileTask int task) { - return mPendingTasks.contains(task); - } - - @VisibleForTesting - TileSetupDelegate getTileSetupDelegate() { - return mTileSetupDelegate; - } - - @Nullable - public SiteSuggestion getHomepageTileData() { - for (Tile tile : mTileSections.get(TileSectionType.PERSONALIZED)) { - if (tile.getSource() == TileSource.HOMEPAGE) { - return tile.getData(); - } - } - return null; - } - - private static SparseArray<List<Tile>> createEmptyTileData() { - SparseArray<List<Tile>> newTileData = new SparseArray<>(); - - // TODO(dgn): How do we want to handle empty states and sections that have no tiles? - // Have an empty list for now that can be rendered as-is without causing issues or too much - // state checking. We will have to decide if we want empty lists or no section at all for - // the others. - newTileData.put(TileSectionType.PERSONALIZED, new ArrayList<Tile>()); - - return newTileData; - } - - // TODO(dgn): I would like to move that to TileRenderer, but setting the data on the tile, - // notifying the observer and updating the tasks make it awkward. - private class LargeIconCallbackImpl implements LargeIconBridge.LargeIconCallback { - private final SiteSuggestion mSiteData; - private final boolean mTrackLoadTask; - - private LargeIconCallbackImpl(SiteSuggestion suggestion, boolean trackLoadTask) { - mSiteData = suggestion; - mTrackLoadTask = trackLoadTask; - } - - @Override - public void onLargeIconAvailable(@Nullable Bitmap icon, int fallbackColor, - boolean isFallbackColorDefault, @IconType int iconType) { - Tile tile = findTile(mSiteData); - if (tile != null) { // Do nothing if the tile was removed. - tile.setIconType(iconType); - if (icon == null) { - mTileRenderer.setTileIconFromColor(tile, fallbackColor, isFallbackColorDefault); - } else { - mTileRenderer.setTileIconFromBitmap(tile, icon); - } - - mObserver.onTileIconChanged(tile); - } - - // This call needs to be made after the tiles are completely initialised, for UMA. - if (mTrackLoadTask) removeTask(TileTask.FETCH_ICON); - } - } - - /** - * Implements various listener and delegate interfaces to handle user interactions with tiles. - */ - public class TileInteractionDelegate - implements ContextMenuManager.Delegate, OnClickListener, OnCreateContextMenuListener { - private final SiteSuggestion mSuggestion; - private Runnable mOnClickRunnable; - - public TileInteractionDelegate(SiteSuggestion suggestion) { - mSuggestion = suggestion; - } - - @Override - public void onClick(View view) { - Tile tile = findTile(mSuggestion); - if (tile == null) return; - - SuggestionsMetrics.recordTileTapped(); - if (mOnClickRunnable != null) mOnClickRunnable.run(); - mTileGroupDelegate.openMostVisitedItem(WindowOpenDisposition.CURRENT_TAB, tile); - } - - @Override - public void openItem(int windowDisposition) { - Tile tile = findTile(mSuggestion); - if (tile == null) return; - - mTileGroupDelegate.openMostVisitedItem(windowDisposition, tile); - } - - @Override - public void removeItem() { - Tile tile = findTile(mSuggestion); - if (tile == null) return; - - // Note: This does not track all the removals, but will track the most recent one. If - // that removal is committed, it's good enough for change detection. - mPendingRemovalUrl = mSuggestion.url; - mTileGroupDelegate.removeMostVisitedItem(tile, url -> mPendingInsertionUrl = url); - } - - @Override - public String getUrl() { - return mSuggestion.url; - } - - @Override - public String getContextMenuTitle() { - return null; - } - - @Override - public boolean isItemSupported(@ContextMenuItemId int menuItemId) { - switch (menuItemId) { - // Personalized tiles are the only tiles that can be removed. Additionally, the - // Explore tile counts as a personalized tile but cannot be removed. - case ContextMenuItemId.REMOVE: - return mSuggestion.sectionType == TileSectionType.PERSONALIZED - && mSuggestion.source != TileSource.EXPLORE; - case ContextMenuItemId.LEARN_MORE: - return SuggestionsConfig.scrollToLoad(); - case ContextMenuItemId.OPEN_IN_INCOGNITO_TAB: - return mSuggestion.source != TileSource.EXPLORE; - default: - return true; - } - } - - @Override - public void onContextMenuCreated() {} - - @Override - public void onCreateContextMenu( - ContextMenu contextMenu, View view, ContextMenuInfo contextMenuInfo) { - mContextMenuManager.createContextMenu(contextMenu, view, this); - } - - /** - * Set a runnable for click events on the tile. This is primarily used to track interaction - * with the tile used by feature engagement purposes. - * @param clickRunnable The {@link Runnable} to be executed when tile is clicked. - */ - public void setOnClickRunnable(Runnable clickRunnable) { - mOnClickRunnable = clickRunnable; - } - } - - private class OfflineModelObserver extends SuggestionsOfflineModelObserver<Tile> { - public OfflineModelObserver(OfflinePageBridge bridge) { - super(bridge); - } - - @Override - public void onSuggestionOfflineIdChanged(Tile tile, OfflinePageItem item) { - boolean oldOfflineAvailable = tile.isOfflineAvailable(); - tile.setOfflinePageOfflineId(item == null ? null : item.getOfflineId()); - - // Only notify to update the view if there will be a visible change. - if (oldOfflineAvailable == tile.isOfflineAvailable()) return; - mObserver.onTileOfflineBadgeVisibilityChanged(tile); - } - - @Override - public Iterable<Tile> getOfflinableSuggestions() { - List<Tile> tiles = new ArrayList<>(); - for (int i = 0; i < mTileSections.size(); ++i) tiles.addAll(mTileSections.valueAt(i)); - return tiles; - } - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroupDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroupDelegateImpl.java deleted file mode 100644 index 7c517e9a..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileGroupDelegateImpl.java +++ /dev/null
@@ -1,134 +0,0 @@ -// 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.chrome.browser.suggestions; - -import android.content.Context; - -import org.chromium.base.Callback; -import org.chromium.base.metrics.RecordUserAction; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.ChromeActivity; -import org.chromium.chrome.browser.ntp.NewTabPageUma; -import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.snackbar.Snackbar; -import org.chromium.chrome.browser.snackbar.SnackbarManager; -import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController; -import org.chromium.ui.mojom.WindowOpenDisposition; - -import java.util.List; - -/** - * Reusable implementation of {@link TileGroup.Delegate}. Performs work in parts of the system that - * the {@link TileGroup} should not know about. - */ -public class TileGroupDelegateImpl implements TileGroup.Delegate { - - private final Context mContext; - private final SnackbarManager mSnackbarManager; - private final SuggestionsNavigationDelegate mNavigationDelegate; - private final MostVisitedSites mMostVisitedSites; - - private boolean mIsDestroyed; - private SnackbarController mTileRemovedSnackbarController; - - public TileGroupDelegateImpl(ChromeActivity activity, Profile profile, - SuggestionsNavigationDelegate navigationDelegate, SnackbarManager snackbarManager) { - mContext = activity; - mSnackbarManager = snackbarManager; - mNavigationDelegate = navigationDelegate; - mMostVisitedSites = - SuggestionsDependencyFactory.getInstance().createMostVisitedSites(profile); - } - - @Override - public void removeMostVisitedItem(Tile item, Callback<String> removalUndoneCallback) { - assert !mIsDestroyed; - - mMostVisitedSites.addBlacklistedUrl(item.getUrl()); - showTileRemovedSnackbar(item.getUrl(), removalUndoneCallback); - } - - @Override - public void openMostVisitedItem(int windowDisposition, Tile item) { - assert !mIsDestroyed; - - String url = item.getUrl(); - - // TODO(treib): Should we call recordOpenedMostVisitedItem here? - if (windowDisposition != WindowOpenDisposition.NEW_WINDOW) { - recordOpenedTile(item); - } - - mNavigationDelegate.navigateToSuggestionUrl(windowDisposition, url); - } - - @Override - public void setMostVisitedSitesObserver(MostVisitedSites.Observer observer, int maxResults) { - assert !mIsDestroyed; - - mMostVisitedSites.setObserver(observer, maxResults); - } - - @Override - public void onLoadingComplete(List<Tile> tiles) { - // This method is called after network calls complete. It could happen after the suggestions - // surface is destroyed. - if (mIsDestroyed) return; - - for (Tile tile : tiles) { - mMostVisitedSites.recordTileImpression(tile); - } - - mMostVisitedSites.recordPageImpression(tiles.size()); - - for (Tile tile : tiles) { - if (tile.isOfflineAvailable()) { - SuggestionsMetrics.recordTileOfflineAvailability(tile.getIndex()); - } - } - } - - @Override - public void destroy() { - assert !mIsDestroyed; - mIsDestroyed = true; - - if (mTileRemovedSnackbarController != null) { - mSnackbarManager.dismissSnackbars(mTileRemovedSnackbarController); - } - mMostVisitedSites.destroy(); - } - - private void showTileRemovedSnackbar(String url, final Callback<String> removalUndoneCallback) { - if (mTileRemovedSnackbarController == null) { - mTileRemovedSnackbarController = new SnackbarController() { - @Override - public void onDismissNoAction(Object actionData) {} - - /** Undoes the tile removal. */ - @Override - public void onAction(Object actionData) { - if (mIsDestroyed) return; - String url = (String) actionData; - removalUndoneCallback.onResult(url); - mMostVisitedSites.removeBlacklistedUrl(url); - } - }; - } - Snackbar snackbar = Snackbar.make(mContext.getString(R.string.most_visited_item_removed), - mTileRemovedSnackbarController, Snackbar.TYPE_ACTION, - Snackbar.UMA_NTP_MOST_VISITED_DELETE_UNDO) - .setAction(mContext.getString(R.string.undo), url); - mSnackbarManager.showSnackbar(snackbar); - } - - private void recordOpenedTile(Tile tile) { - NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_MOST_VISITED_TILE); - RecordUserAction.record("MobileNTPMostVisited"); - NewTabPageUma.recordExplicitUserNavigation( - tile.getUrl(), NewTabPageUma.RAPPOR_ACTION_VISITED_SUGGESTED_TILE); - mMostVisitedSites.recordOpenedMostVisitedItem(tile); - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java deleted file mode 100644 index b916b01..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/TileRenderer.java +++ /dev/null
@@ -1,283 +0,0 @@ -// 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.chrome.browser.suggestions; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.graphics.drawable.BitmapDrawable; -import android.support.annotation.LayoutRes; -import android.support.graphics.drawable.VectorDrawableCompat; -import android.support.v4.graphics.drawable.RoundedBitmapDrawable; -import android.view.LayoutInflater; -import android.view.ViewGroup; - -import org.chromium.base.ApiCompatibilityUtils; -import org.chromium.base.Log; -import org.chromium.base.VisibleForTesting; -import org.chromium.base.task.AsyncTask; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge; -import org.chromium.chrome.browser.explore_sites.ExploreSitesIPH; -import org.chromium.chrome.browser.explore_sites.MostLikelyVariation; -import org.chromium.chrome.browser.favicon.IconType; -import org.chromium.chrome.browser.favicon.LargeIconBridge; -import org.chromium.chrome.browser.feature_engagement.TrackerFactory; -import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle; -import org.chromium.chrome.browser.suggestions.tile.TopSitesTileView; -import org.chromium.chrome.browser.util.ViewUtils; -import org.chromium.chrome.browser.widget.RoundedIconGenerator; -import org.chromium.components.feature_engagement.EventConstants; -import org.chromium.components.feature_engagement.Tracker; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Utility class that renders {@link Tile}s into a provided {@link ViewGroup}, creating and - * manipulating the views as needed. - */ -public class TileRenderer { - private static final String TAG = "TileRenderer"; - - private final Resources mResources; - private final ImageFetcher mImageFetcher; - private final RoundedIconGenerator mIconGenerator; - private final Resources.Theme mTheme; - - @TileStyle - private final int mStyle; - private final int mTitleLinesCount; - private final int mDesiredIconSize; - private final int mMinIconSize; - private final float mIconCornerRadius; - - @LayoutRes - private final int mLayout; - - @LayoutRes - private final int mTopSitesLayout; - - public TileRenderer( - Context context, @TileStyle int style, int titleLines, ImageFetcher imageFetcher) { - mImageFetcher = imageFetcher; - mStyle = style; - mTitleLinesCount = titleLines; - - mResources = context.getResources(); - mTheme = context.getTheme(); - mDesiredIconSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_size); - mIconCornerRadius = mResources.getDimension(R.dimen.tile_view_icon_corner_radius); - int minIconSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_min_size); - - // On ldpi devices, mDesiredIconSize could be even smaller than the global limit. - mMinIconSize = Math.min(mDesiredIconSize, minIconSize); - - mLayout = getLayout(); - mTopSitesLayout = getTopSitesLayout(); - - int iconColor = ApiCompatibilityUtils.getColor( - mResources, R.color.default_favicon_background_color); - int iconTextSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_text_size); - mIconGenerator = new RoundedIconGenerator( - mDesiredIconSize, mDesiredIconSize, mDesiredIconSize / 2, iconColor, iconTextSize); - } - - /** - * Renders tile views in the given {@link ViewGroup}, reusing existing tile views where - * possible because view inflation and icon loading are slow. - * @param parent The layout to render the tile views into. - * @param sectionTiles Tiles to render. - * @param setupDelegate Delegate used to setup callbacks and listeners for the new views. - */ - public void renderTileSection( - List<Tile> sectionTiles, ViewGroup parent, TileGroup.TileSetupDelegate setupDelegate) { - // Map the old tile views by url so they can be reused later. - Map<SiteSuggestion, SuggestionsTileView> oldTileViews = new HashMap<>(); - int childCount = parent.getChildCount(); - for (int i = 0; i < childCount; i++) { - SuggestionsTileView tileView = (SuggestionsTileView) parent.getChildAt(i); - oldTileViews.put(tileView.getData(), tileView); - } - - // Remove all views from the layout because even if they are reused later they'll have to be - // added back in the correct order. - parent.removeAllViews(); - - for (Tile tile : sectionTiles) { - SuggestionsTileView tileView = oldTileViews.get(tile.getData()); - if (tileView == null) { - tileView = buildTileView(tile, parent, setupDelegate); - } - - parent.addView(tileView); - } - } - - /** - * Record that a tile was clicked for IPH reasons. - */ - private void recordTileClickedForIPH(String eventName) { - Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile()); - tracker.notifyEvent(eventName); - } - - /** - * Inflates a new tile view, initializes it, and loads an icon for it. - * @param tile The tile that holds the data to populate the new tile view. - * @param parentView The parent of the new tile view. - * @param setupDelegate The delegate used to setup callbacks and listeners for the new view. - * @return The new tile view. - */ - @VisibleForTesting - SuggestionsTileView buildTileView( - Tile tile, ViewGroup parentView, TileGroup.TileSetupDelegate setupDelegate) { - SuggestionsTileView tileView; - - if (tile.getSource() == TileSource.EXPLORE) { - tileView = (TopSitesTileView) LayoutInflater.from(parentView.getContext()) - .inflate(mTopSitesLayout, parentView, false); - - int iconVariation = ExploreSitesBridge.getIconVariation(); - if (iconVariation == MostLikelyVariation.ICON_ARROW) { - tile.setIcon(VectorDrawableCompat.create( - mResources, R.drawable.ic_arrow_forward_blue_24dp, mTheme)); - tile.setType(TileVisualType.ICON_REAL); - } else if (iconVariation == MostLikelyVariation.ICON_DOTS) { - tile.setIcon(VectorDrawableCompat.create( - mResources, R.drawable.ic_apps_blue_24dp, mTheme)); - tile.setType(TileVisualType.ICON_REAL); - } else if (iconVariation == MostLikelyVariation.ICON_GROUPED) { - tile.setIcon(VectorDrawableCompat.create( - mResources, R.drawable.ic_apps_blue_24dp, mTheme)); - tile.setType(TileVisualType.ICON_DEFAULT); - - // One task to load actual icon. - LargeIconBridge.LargeIconCallback bridgeCallback = - setupDelegate.createIconLoadCallback(tile); - ExploreSitesBridge.getSummaryImage(Profile.getLastUsedProfile(), mDesiredIconSize, - (Bitmap img) - -> bridgeCallback.onLargeIconAvailable( - img, Color.BLACK, false, IconType.FAVICON)); - } - } else { - tileView = (SuggestionsTileView) LayoutInflater.from(parentView.getContext()) - .inflate(mLayout, parentView, false); - } - - tileView.initialize(tile, mTitleLinesCount); - - // Note: It is important that the callbacks below don't keep a reference to the tile or - // modify them as there is no guarantee that the same tile would be used to update the view. - if (tile.getSource() != TileSource.EXPLORE) { - fetchIcon(tile.getData(), setupDelegate.createIconLoadCallback(tile)); - } - - TileGroup.TileInteractionDelegate delegate = setupDelegate.createInteractionDelegate(tile); - if (tile.getSource() == TileSource.HOMEPAGE) { - delegate.setOnClickRunnable( - () -> recordTileClickedForIPH(EventConstants.HOMEPAGE_TILE_CLICKED)); - } else if (tile.getSource() == TileSource.EXPLORE) { - delegate.setOnClickRunnable( - () -> recordTileClickedForIPH(EventConstants.EXPLORE_SITES_TILE_TAPPED)); - } - - tileView.setOnClickListener(delegate); - tileView.setOnCreateContextMenuListener(delegate); - - if (tile.getSource() == TileSource.EXPLORE) { - ExploreSitesIPH.configureIPH(tileView, Profile.getLastUsedProfile()); - } - - return tileView; - } - - private void fetchIcon( - final SiteSuggestion siteData, final LargeIconBridge.LargeIconCallback iconCallback) { - if (siteData.whitelistIconPath.isEmpty()) { - mImageFetcher.makeLargeIconRequest(siteData.url, mMinIconSize, iconCallback); - return; - } - - AsyncTask<Bitmap> task = new AsyncTask<Bitmap>() { - @Override - protected Bitmap doInBackground() { - Bitmap bitmap = BitmapFactory.decodeFile(siteData.whitelistIconPath); - if (bitmap == null) { - Log.d(TAG, "Image decoding failed: %s", siteData.whitelistIconPath); - } - return bitmap; - } - - @Override - protected void onPostExecute(Bitmap icon) { - if (icon == null) { - mImageFetcher.makeLargeIconRequest(siteData.url, mMinIconSize, iconCallback); - } else { - iconCallback.onLargeIconAvailable(icon, Color.BLACK, false, IconType.INVALID); - } - } - }; - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - public void updateIcon( - SiteSuggestion siteData, LargeIconBridge.LargeIconCallback iconCallback) { - mImageFetcher.makeLargeIconRequest(siteData.url, mMinIconSize, iconCallback); - } - - public void setTileIconFromBitmap(Tile tile, Bitmap icon) { - int radius = Math.round(mIconCornerRadius * icon.getWidth() / mDesiredIconSize); - if (tile.getSource() == TileSource.EXPLORE) { - radius = mDesiredIconSize / 2; - } - RoundedBitmapDrawable roundedIcon = ViewUtils.createRoundedBitmapDrawable(icon, radius); - roundedIcon.setAntiAlias(true); - roundedIcon.setFilterBitmap(true); - - tile.setIcon(roundedIcon); - tile.setType(TileVisualType.ICON_REAL); - } - - public void setTileIconFromColor(Tile tile, int fallbackColor, boolean isFallbackColorDefault) { - // Explore should not have generated icons. - if (tile.getSource() == TileSource.EXPLORE) { - return; - } - mIconGenerator.setBackgroundColor(fallbackColor); - Bitmap icon = mIconGenerator.generateIconForUrl(tile.getUrl()); - tile.setIcon(new BitmapDrawable(mResources, icon)); - tile.setType( - isFallbackColorDefault ? TileVisualType.ICON_DEFAULT : TileVisualType.ICON_COLOR); - } - - @LayoutRes - private int getLayout() { - switch (mStyle) { - case TileStyle.MODERN: - return R.layout.suggestions_tile_view; - case TileStyle.MODERN_CONDENSED: - return R.layout.suggestions_tile_view_condensed; - } - assert false; - return 0; - } - - @LayoutRes - private int getTopSitesLayout() { - switch (mStyle) { - case TileStyle.MODERN: - return R.layout.top_sites_tile_view; - case TileStyle.MODERN_CONDENSED: - return R.layout.top_sites_tile_view_condensed; - } - assert false; - return 0; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSites.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSites.java new file mode 100644 index 0000000..9c345f9f --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSites.java
@@ -0,0 +1,94 @@ +// 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.chrome.browser.suggestions.mostvisited; + +import android.support.annotation.Nullable; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.chrome.browser.suggestions.SiteSuggestion; +import org.chromium.chrome.browser.suggestions.tile.Tile; + +import java.util.List; + +/** + * Methods to provide most recent urls, titles and thumbnails. + */ +public interface MostVisitedSites { + /** + * An interface for handling events in {@link MostVisitedSites}. + */ + interface Observer { + /** This is called when the list of most visited URLs is initially available or updated. */ + void onSiteSuggestionsAvailable(List<SiteSuggestion> siteSuggestions); + + /** + * This is called when a previously uncached icon has been fetched. + * Parameters guaranteed to be non-null. + * + * @param siteUrl URL of site with newly-cached icon. + */ + void onIconMadeAvailable(String siteUrl); + } + + /** + * An interface to provide {@link MostVisitedSites} with platform-specific home page data. + */ + interface HomepageClient { + /** + * @return True if homepage tile should be shown. + */ + @CalledByNative("HomepageClient") + boolean isHomepageTileEnabled(); + + /** + * @return The raw URL of the currently set home page. + */ + @CalledByNative("HomepageClient") + @Nullable + String getHomepageUrl(); + } + + /** + * This instance must not be used after calling destroy(). + */ + void destroy(); + + /** + * Sets the recipient for events from {@link MostVisitedSites}. The observer may be notified + * synchronously or asynchronously. + * @param observer The observer to be notified. + * @param numSites The maximum number of sites to return. + */ + void setObserver(Observer observer, int numSites); + + /** + * Blacklists a URL from the most visited URLs list. + */ + void addBlacklistedUrl(String url); + + /** + * Removes a URL from the most visited URLs blacklist. + */ + void removeBlacklistedUrl(String url); + + /** + * Records metrics about an impression of the surface with tiles. + * @param tilesCount Count of tiles available on the surface at the moment. + */ + void recordPageImpression(int tilesCount); + + /** + * Records metrics about an impression of a tile including its source (local, server, ...) and + * its visual type. + * @param tile Object holding the details of a tile. + */ + void recordTileImpression(Tile tile); + + /** + * Records the opening of a Most Visited Item. + * @param tile Object holding the details of a tile. + */ + void recordOpenedMostVisitedItem(Tile tile); +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java new file mode 100644 index 0000000..5276129 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java
@@ -0,0 +1,197 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.suggestions.mostvisited; + +import android.text.TextUtils; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNIAdditionalImport; +import org.chromium.chrome.browser.ntp.NewTabPage; +import org.chromium.chrome.browser.partnercustomizations.HomepageManager; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.suggestions.SiteSuggestion; +import org.chromium.chrome.browser.suggestions.tile.Tile; +import org.chromium.chrome.browser.util.FeatureUtilities; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Methods to bridge into native history to provide most recent urls, titles and thumbnails. + */ +@JNIAdditionalImport(MostVisitedSites.class) // Needed for the Observer usage in the native calls. +public class MostVisitedSitesBridge + implements MostVisitedSites, HomepageManager.HomepageStateListener { + /** + * Maximum number of tiles that is explicitly supported. UMA relies on this value, so even if + * the UI supports it, getting more can raise unexpected issues. + */ + public static final int MAX_TILE_COUNT = 12; + + private long mNativeMostVisitedSitesBridge; + + private MostVisitedSites.Observer mWrappedObserver; + + /** + * MostVisitedSites constructor requires a valid user profile object. + * + * @param profile The profile for which to fetch most visited sites. + */ + public MostVisitedSitesBridge(Profile profile) { + mNativeMostVisitedSitesBridge = nativeInit(profile); + // The first tile replaces is replaced with homepage tile if NTPButton is enabled. Setting + // a homepage client to provide Java side information. + if (FeatureUtilities.isNewTabPageButtonEnabled() + && FeatureUtilities.isHomepageTileEnabled()) { + nativeSetHomepageClient(mNativeMostVisitedSitesBridge, new HomepageClient() { + @Override + public boolean isHomepageTileEnabled() { + return HomepageManager.isHomepageEnabled() + && !NewTabPage.isNTPUrl(getHomepageUrl()) + && !TextUtils.isEmpty(HomepageManager.getHomepageUri()); + } + + @Override + public String getHomepageUrl() { + return HomepageManager.getHomepageUri(); + } + }); + HomepageManager.getInstance().addListener(this); + } + } + + /** + * Cleans up the C++ side of this class. This instance must not be used after calling destroy(). + */ + @Override + public void destroy() { + // Stop listening even if it was not started in the first place. (Handled without errors.) + HomepageManager.getInstance().removeListener(this); + assert mNativeMostVisitedSitesBridge != 0; + nativeDestroy(mNativeMostVisitedSitesBridge); + mNativeMostVisitedSitesBridge = 0; + } + + @Override + public void setObserver(Observer observer, int numSites) { + assert numSites <= MAX_TILE_COUNT; + mWrappedObserver = observer; + + nativeSetObserver(mNativeMostVisitedSitesBridge, this, numSites); + } + + @Override + public void addBlacklistedUrl(String url) { + nativeAddOrRemoveBlacklistedUrl(mNativeMostVisitedSitesBridge, url, true); + } + + @Override + public void removeBlacklistedUrl(String url) { + nativeAddOrRemoveBlacklistedUrl(mNativeMostVisitedSitesBridge, url, false); + } + + @Override + public void recordPageImpression(int tilesCount) { + nativeRecordPageImpression(mNativeMostVisitedSitesBridge, tilesCount); + } + + @Override + public void recordTileImpression(Tile tile) { + nativeRecordTileImpression(mNativeMostVisitedSitesBridge, tile.getIndex(), tile.getType(), + tile.getIconType(), tile.getTitleSource(), tile.getSource(), + tile.getData().dataGenerationTime.getTime(), tile.getUrl()); + } + + @Override + public void recordOpenedMostVisitedItem(Tile tile) { + nativeRecordOpenedMostVisitedItem(mNativeMostVisitedSitesBridge, tile.getIndex(), + tile.getType(), tile.getTitleSource(), tile.getSource(), + tile.getData().dataGenerationTime.getTime()); + } + + @Override + public void onHomepageStateUpdated() { + assert mNativeMostVisitedSitesBridge != 0; + // Ensure even a blacklisted homepage can be set as tile when (re-)enabling it. + if (HomepageManager.isHomepageEnabled()) { + removeBlacklistedUrl(HomepageManager.getHomepageUri()); + } + nativeOnHomepageStateChanged(mNativeMostVisitedSitesBridge); + } + + /** + * Utility function to convert JNI friendly site suggestion data to a Java friendly list of + * {@link SiteSuggestion}s. + */ + public static List<SiteSuggestion> buildSiteSuggestions(String[] titles, String[] urls, + int[] sections, String[] whitelistIconPaths, int[] titleSources, int[] sources, + long[] dataGenerationTimesMs) { + List<SiteSuggestion> siteSuggestions = new ArrayList<>(titles.length); + for (int i = 0; i < titles.length; ++i) { + siteSuggestions.add(new SiteSuggestion(titles[i], urls[i], whitelistIconPaths[i], + titleSources[i], sources[i], sections[i], new Date(dataGenerationTimesMs[i]))); + } + return siteSuggestions; + } + + /** + * This is called when the list of most visited URLs is initially available or updated. + * Parameters guaranteed to be non-null. + * + * @param titles Array of most visited url page titles. + * @param urls Array of most visited URLs, including popular URLs if + * available and necessary (i.e. there aren't enough most + * visited URLs). + * @param whitelistIconPaths The paths to the icon image files for whitelisted tiles, empty + * strings otherwise. + * @param sources For each tile, the {@code TileSource} that generated the tile. + */ + @CalledByNative + private void onURLsAvailable(String[] titles, String[] urls, int[] sections, + String[] whitelistIconPaths, int[] titleSources, int[] sources, + long[] dataGenerationTimesMs) { + // Don't notify observer if we've already been destroyed. + if (mNativeMostVisitedSitesBridge == 0) return; + + List<SiteSuggestion> suggestions = new ArrayList<>(); + + suggestions.addAll(buildSiteSuggestions(titles, urls, sections, whitelistIconPaths, + titleSources, sources, dataGenerationTimesMs)); + + mWrappedObserver.onSiteSuggestionsAvailable(suggestions); + } + + /** + * This is called when a previously uncached icon has been fetched. + * Parameters guaranteed to be non-null. + * + * @param siteUrl URL of site with newly-cached icon. + */ + @CalledByNative + private void onIconMadeAvailable(String siteUrl) { + // Don't notify observer if we've already been destroyed. + if (mNativeMostVisitedSitesBridge != 0) { + mWrappedObserver.onIconMadeAvailable(siteUrl); + } + } + + private native long nativeInit(Profile profile); + private native void nativeDestroy(long nativeMostVisitedSitesBridge); + private native void nativeOnHomepageStateChanged(long nativeMostVisitedSitesBridge); + private native void nativeSetHomepageClient( + long nativeMostVisitedSitesBridge, MostVisitedSites.HomepageClient homePageClient); + private native void nativeSetObserver( + long nativeMostVisitedSitesBridge, MostVisitedSitesBridge observer, int numSites); + private native void nativeAddOrRemoveBlacklistedUrl( + long nativeMostVisitedSitesBridge, String url, boolean addUrl); + private native void nativeRecordPageImpression( + long nativeMostVisitedSitesBridge, int tilesCount); + private native void nativeRecordTileImpression(long nativeMostVisitedSitesBridge, int index, + int type, int iconType, int titleSource, int source, long dataGenerationTimeMs, + String url); + private native void nativeRecordOpenedMostVisitedItem(long nativeMostVisitedSitesBridge, + int index, int tileType, int titleSource, int source, long dataGenerationTimeMs); +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/OWNERS new file mode 100644 index 0000000..125bc14 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/OWNERS
@@ -0,0 +1 @@ +file://chrome/browser/android/explore_sites/OWNERS \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSection.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSection.java new file mode 100644 index 0000000..467575cf --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSection.java
@@ -0,0 +1,125 @@ +// 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.chrome.browser.suggestions.tile; + +import android.support.annotation.LayoutRes; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import org.chromium.base.ContextUtils; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeFeatureList; +import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge; +import org.chromium.chrome.browser.native_page.ContextMenuManager; +import org.chromium.chrome.browser.ntp.cards.ItemViewType; +import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder; +import org.chromium.chrome.browser.ntp.cards.OptionalLeaf; +import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; +import org.chromium.chrome.browser.suggestions.SuggestionsConfig; +import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; + +/** + * The model and controller for a group of site suggestions. + * @deprecated This class is still being used, but not in the New Tab Page RecyclerView + * anymore. It still uses the latter's base classes until SiteSection is migrated to the new + * UI architecture. + */ +@Deprecated +public class SiteSection extends OptionalLeaf implements TileGroup.Observer { + /** + * The maximum number of tiles to try and fit in a row. On smaller screens, there may not be + * enough space to fit all of them. + */ + private static final int MAX_TILE_COLUMNS = 4; + private static final int TILE_TITLE_LINES = 1; + + private final TileGroup mTileGroup; + private final TileRenderer mTileRenderer; + + public static ViewGroup inflateSiteSection(ViewGroup parent) { + return (ViewGroup) LayoutInflater.from(parent.getContext()) + .inflate(getLayout(), parent, false); + } + + public static SiteSectionViewHolder createViewHolder(ViewGroup view, UiConfig uiConfig) { + return new TileGridViewHolder(view, getMaxTileRows(), MAX_TILE_COLUMNS); + } + + public SiteSection(SuggestionsUiDelegate uiDelegate, ContextMenuManager contextMenuManager, + TileGroup.Delegate tileGroupDelegate, OfflinePageBridge offlinePageBridge, + UiConfig uiConfig) { + mTileRenderer = new TileRenderer(ContextUtils.getApplicationContext(), + SuggestionsConfig.getTileStyle(uiConfig), TILE_TITLE_LINES, + uiDelegate.getImageFetcher()); + mTileGroup = new TileGroup(mTileRenderer, uiDelegate, contextMenuManager, tileGroupDelegate, + /* observer = */ this, offlinePageBridge); + mTileGroup.startObserving(MAX_TILE_COLUMNS * getMaxTileRows()); + } + + @Override + @ItemViewType + protected int getItemViewType() { + // Throw an exception instead of just `assert false` to avoid compiler warnings about the + // return value. + throw new IllegalStateException(); + } + + @Override + protected void onBindViewHolder(NewTabPageViewHolder holder) { + SiteSectionViewHolder siteSectionView = (SiteSectionViewHolder) holder; + siteSectionView.bindDataSource(mTileGroup, mTileRenderer); + siteSectionView.refreshData(); + } + + @Override + public String describeForTesting() { + // Throw an exception instead of just `assert false` to avoid compiler warnings about the + // return value. + throw new IllegalStateException(); + } + + @Override + public void onTileDataChanged() { + setVisibilityInternal(!mTileGroup.isEmpty()); + if (!isVisible()) return; + notifyItemChanged(0, (holder) -> ((SiteSectionViewHolder) holder).refreshData()); + } + + @Override + public void onTileCountChanged() { + onTileDataChanged(); + } + + @Override + public void onTileIconChanged(Tile tile) { + if (!isVisible()) return; + notifyItemChanged(0, (holder) -> ((SiteSectionViewHolder) holder).updateIconView(tile)); + } + + @Override + public void onTileOfflineBadgeVisibilityChanged(Tile tile) { + if (!isVisible()) return; + notifyItemChanged(0, (holder) -> ((SiteSectionViewHolder) holder).updateOfflineBadge(tile)); + } + + TileGroup getTileGroupForTesting() { + return mTileGroup; + } + + private static int getMaxTileRows() { + if (ChromeFeatureList.isEnabled(ChromeFeatureList.EXPLORE_SITES) + && !ExploreSitesBridge.isIntegratedWithMostLikely( + ExploreSitesBridge.getVariation())) { + return 1; + } + return 2; + } + + @LayoutRes + private static int getLayout() { + return R.layout.suggestions_site_tile_grid_modern; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSectionViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSectionViewHolder.java new file mode 100644 index 0000000..63c31d9e --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SiteSectionViewHolder.java
@@ -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. + +package org.chromium.chrome.browser.suggestions.tile; + +import android.support.annotation.CallSuper; +import android.support.annotation.Nullable; +import android.view.View; + +import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder; +import org.chromium.chrome.browser.suggestions.SiteSuggestion; + +/** + * Describes a portion of UI responsible for rendering a group of sites. It abstracts general tasks + * related to initialising and updating this UI. + */ +public abstract class SiteSectionViewHolder extends NewTabPageViewHolder { + protected TileGroup mTileGroup; + protected TileRenderer mTileRenderer; + + /** + * Constructs a {@link SiteSectionViewHolder} used to display tiles in both NTP and Chrome Home. + * + * @param itemView The {@link View} for this item + */ + public SiteSectionViewHolder(View itemView) { + super(itemView); + } + + /** Initialise the view, letting it know the data it will have to display. */ + @CallSuper + public void bindDataSource(TileGroup tileGroup, TileRenderer tileRenderer) { + mTileGroup = tileGroup; + mTileRenderer = tileRenderer; + } + + /** + * Sets a new icon on the child view with a matching site. + * @param tile The tile that holds the data to populate the tile view. + */ + public void updateIconView(Tile tile) { + SuggestionsTileView tileView = findTileView(tile.getData()); + if (tileView != null) tileView.renderIcon(tile); + } + + /** + * Updates the visibility of the offline badge on the child view with a matching site. + * @param tile The tile that holds the data to populate the tile view. + */ + public void updateOfflineBadge(Tile tile) { + SuggestionsTileView tileView = findTileView(tile.getData()); + if (tileView != null) tileView.renderOfflineBadge(tile); + } + + /** Clears the current data and displays the current state of the model. */ + public abstract void refreshData(); + + @Nullable + public abstract SuggestionsTileView findTileView(SiteSuggestion data); +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java new file mode 100644 index 0000000..a00bb6e --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java
@@ -0,0 +1,81 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.suggestions.tile; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; + +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ntp.TitleUtil; +import org.chromium.chrome.browser.suggestions.SiteSuggestion; +import org.chromium.chrome.browser.widget.tile.TileWithTextView; + +/** + * The view for a site suggestion tile. Displays the title of the site beneath a large icon. If a + * large icon isn't available, displays a rounded rectangle with a single letter in its place. + */ +public class SuggestionsTileView extends TileWithTextView { + /** The data currently associated to this tile. */ + private SiteSuggestion mData; + + /** + * Constructor for inflating from XML. + */ + public SuggestionsTileView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** + * Initializes the view using the data held by {@code tile}. This should be called immediately + * after inflation. + * @param tile The tile that holds the data to populate this view. + * @param titleLines The number of text lines to use for the tile title. + */ + public void initialize(Tile tile, int titleLines) { + super.initialize(TitleUtil.getTitleForDisplay(tile.getTitle(), tile.getUrl()), + tile.isOfflineAvailable(), tile.getIcon(), titleLines); + mData = tile.getData(); + setIconViewLayoutParams(tile); + } + + /** Retrieves data associated with this view. */ + public SiteSuggestion getData() { + return mData; + } + + /** Retrieves url associated with this view. */ + public String getUrl() { + return mData.url; + } + + /** Renders icon based on tile data. */ + public void renderIcon(Tile tile) { + setIconDrawable(tile.getIcon()); + setIconViewLayoutParams(tile); + } + + public void renderOfflineBadge(Tile tile) { + setOfflineBadgeVisibility(tile.isOfflineAvailable()); + } + + protected void setIconViewLayoutParams(Tile tile) { + MarginLayoutParams params = (MarginLayoutParams) mIconView.getLayoutParams(); + Resources resources = getResources(); + if (tile.getType() == TileVisualType.ICON_COLOR + || tile.getType() == TileVisualType.ICON_DEFAULT) { + params.width = resources.getDimensionPixelSize(R.dimen.tile_view_monogram_size_modern); + params.height = resources.getDimensionPixelSize(R.dimen.tile_view_monogram_size_modern); + params.topMargin = + resources.getDimensionPixelSize(R.dimen.tile_view_monogram_margin_top_modern); + } else { + params.width = resources.getDimensionPixelSize(R.dimen.tile_view_icon_size_modern); + params.height = resources.getDimensionPixelSize(R.dimen.tile_view_icon_size_modern); + params.topMargin = + resources.getDimensionPixelSize(R.dimen.tile_view_icon_margin_top_modern); + } + mIconView.setLayoutParams(params); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/Tile.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/Tile.java new file mode 100644 index 0000000..4cff7eb3 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/Tile.java
@@ -0,0 +1,157 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.suggestions.tile; + +import android.graphics.drawable.Drawable; +import android.support.annotation.Nullable; + +import org.chromium.chrome.browser.favicon.IconType; +import org.chromium.chrome.browser.suggestions.OfflinableSuggestion; +import org.chromium.chrome.browser.suggestions.SiteSuggestion; + +/** + * Holds the details to populate a site suggestion tile. + */ +public class Tile implements OfflinableSuggestion { + private final SiteSuggestion mSiteData; + + private final int mIndex; + + @TileVisualType + private int mType = TileVisualType.NONE; + + @IconType + private int mIconType = IconType.INVALID; + + @Nullable + private Drawable mIcon; + + @Nullable + private Long mOfflinePageOfflineId; + + /** + * @param suggestion The site data we want to populate the tile with. + * @param index The index of this tile in the list of tiles. + */ + public Tile(SiteSuggestion suggestion, int index) { + mSiteData = suggestion; + mIndex = index; + } + + public SiteSuggestion getData() { + return mSiteData; + } + + @Override + public String getUrl() { + return mSiteData.url; + } + + @Override + public void setOfflinePageOfflineId(@Nullable Long offlineId) { + mOfflinePageOfflineId = offlineId; + } + + @Nullable + @Override + public Long getOfflinePageOfflineId() { + return mOfflinePageOfflineId; + } + + @Override + public boolean requiresExactOfflinePage() { + return false; + } + + /** + * @return The title of this tile. + */ + public String getTitle() { + return mSiteData.title; + } + + /** + * @return Whether this tile is available offline. + */ + public boolean isOfflineAvailable() { + return getOfflinePageOfflineId() != null; + } + + /** + * @return The index of this tile in the list of tiles. + */ + public int getIndex() { + return mIndex; + } + + /** + * @return The source of this tile's title. Used for metrics tracking. Valid values are listed + * in {@code TileTitleSource}. + */ + @TileTitleSource + public int getTitleSource() { + return mSiteData.titleSource; + } + + /** + * @return The source of this tile. Used for metrics tracking. Valid values are listed in + * {@code TileSource}. + */ + @TileSource + public int getSource() { + return mSiteData.source; + } + + /** + * @return The visual type of this tile. Valid values are listed in {@link TileVisualType}. + */ + @TileVisualType + public int getType() { + return mType; + } + + /** + * Sets the visual type of this tile. Valid values are listed in + * {@link TileVisualType}. + */ + public void setType(@TileVisualType int type) { + mType = type; + } + + /** + * @return The icon type of this tile. Valid values are listed in {@link IconType}. + */ + @IconType + public int getIconType() { + return mIconType; + } + + /** + * Sets the icon type of this tile. Valid values are listed in {@link IconType}. + */ + public void setIconType(@IconType int iconType) { + mIconType = iconType; + } + + /** + * @return The icon, may be null. + */ + @Nullable + public Drawable getIcon() { + return mIcon; + } + + /** + * Updates the icon drawable. + */ + public void setIcon(@Nullable Drawable icon) { + mIcon = icon; + } + + @TileSectionType + public int getSectionType() { + return mSiteData.sectionType; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayout.java new file mode 100644 index 0000000..462d50eb --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayout.java
@@ -0,0 +1,176 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.suggestions.tile; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.util.Pair; +import android.view.View; +import android.widget.FrameLayout; + +import org.chromium.base.VisibleForTesting; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.suggestions.SiteSuggestion; +import org.chromium.chrome.browser.util.MathUtils; + +/** + * A layout that arranges tiles in a grid. + */ +public class TileGridLayout extends FrameLayout { + private final int mVerticalSpacing; + private final int mMinHorizontalSpacing; + private final int mMaxHorizontalSpacing; + private final int mMaxWidth; + + private int mMaxRows; + private int mMaxColumns; + + /** + * Constructor for inflating from XML. + * + * @param context The view context in which this item will be shown. + * @param attrs The attributes of the XML tag that is inflating the view. + */ + public TileGridLayout(Context context, AttributeSet attrs) { + super(context, attrs); + + Resources res = getResources(); + mVerticalSpacing = res.getDimensionPixelOffset(R.dimen.tile_grid_layout_vertical_spacing); + TypedArray styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.TileGridLayout); + mMinHorizontalSpacing = styledAttrs.getDimensionPixelOffset( + R.styleable.TileGridLayout_minHorizontalSpacing, + res.getDimensionPixelOffset(R.dimen.tile_grid_layout_min_horizontal_spacing)); + styledAttrs.recycle(); + mMaxHorizontalSpacing = Integer.MAX_VALUE; + mMaxWidth = Integer.MAX_VALUE; + } + + /** + * Sets the maximum number of rows to display. Any items that don't fit will be hidden. + */ + public void setMaxRows(int rows) { + mMaxRows = rows; + } + + /** + * Sets the maximum number of columns to display. Any items that don't fit will be hidden. + */ + public void setMaxColumns(int columns) { + mMaxColumns = columns; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int totalWidth = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth); + int childCount = getChildCount(); + if (childCount == 0) { + setMeasuredDimension(totalWidth, resolveSize(0, heightMeasureSpec)); + return; + } + + // Measure the children. We don't use the ViewGroup.measureChildren() method here because + // it only measures visible children. In a situation where a child is invisible before + // this measurement and we decide to show it after the measurement, it will not have its + // dimensions and will not be displayed. + for (int i = 0; i < childCount; i++) { + measureChild(getChildAt(i), MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + } + + // Determine the number of columns that will fit. + int childHeight = getChildAt(0).getMeasuredHeight(); + int childWidth = getChildAt(0).getMeasuredWidth(); + int numColumns = MathUtils.clamp( + (totalWidth + mMinHorizontalSpacing) / (childWidth + mMinHorizontalSpacing), 1, + mMaxColumns); + + // Determine how much padding to use between and around the tiles. + int gridWidthMinusColumns = Math.max(0, totalWidth - numColumns * childWidth); + Pair<Integer, Integer> gridProperties = + computeHorizontalDimensions(true, gridWidthMinusColumns, numColumns); + int gridStart = gridProperties.first; + int horizontalSpacing = gridProperties.second; + + // Limit the number of rows to mMaxRows. + int visibleChildCount = Math.min(childCount, mMaxRows * numColumns); + + // Arrange the visible children in a grid. + int numRows = (visibleChildCount + numColumns - 1) / numColumns; + int paddingTop = getPaddingTop(); + boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; + + for (int i = 0; i < visibleChildCount; i++) { + View child = getChildAt(i); + child.setVisibility(View.VISIBLE); + int row = i / numColumns; + int column = i % numColumns; + int childTop = row * (childHeight + mVerticalSpacing); + int childStart = gridStart + (column * (childWidth + horizontalSpacing)); + MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams(); + layoutParams.setMargins(isRtl ? 0 : childStart, childTop, isRtl ? childStart : 0, 0); + child.setLayoutParams(layoutParams); + } + + // Hide any extra children in case there are more than needed for the maximum number of + // rows. + for (int i = visibleChildCount; i < childCount; i++) { + getChildAt(i).setVisibility(View.GONE); + } + + int totalHeight = paddingTop + getPaddingBottom() + numRows * childHeight + + (numRows - 1) * mVerticalSpacing; + + setMeasuredDimension(totalWidth, resolveSize(totalHeight, heightMeasureSpec)); + } + + /** + * @param spreadTiles Whether to spread the tiles with the same space between and around them. + * @param availableWidth The space available to spread between and around the tiles. + * @param numColumns The number of columns to be organised. + * @return The [gridStart, horizontalSpacing] pair of dimensions. + */ + @VisibleForTesting + Pair<Integer, Integer> computeHorizontalDimensions( + boolean spreadTiles, int availableWidth, int numColumns) { + int gridStart; + float horizontalSpacing; + if (spreadTiles) { + // Identically sized spacers are added both between and around the tiles. + int spacerCount = numColumns + 1; + horizontalSpacing = (float) availableWidth / spacerCount; + gridStart = Math.round(horizontalSpacing); + if (horizontalSpacing < mMinHorizontalSpacing) { + return computeHorizontalDimensions(false, availableWidth, numColumns); + } + } else { + // Ensure column spacing isn't greater than mMaxHorizontalSpacing. + long gridSidePadding = availableWidth - (long) mMaxHorizontalSpacing * (numColumns - 1); + if (gridSidePadding > 0) { + horizontalSpacing = mMaxHorizontalSpacing; + gridStart = (int) (gridSidePadding / 2); + } else { + horizontalSpacing = (float) availableWidth / Math.max(1, numColumns - 1); + gridStart = 0; + } + } + + assert horizontalSpacing >= mMinHorizontalSpacing; + assert horizontalSpacing <= mMaxHorizontalSpacing; + + return Pair.create(gridStart, Math.round(horizontalSpacing)); + } + + @Nullable + public SuggestionsTileView getTileView(SiteSuggestion suggestion) { + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + SuggestionsTileView tileView = (SuggestionsTileView) getChildAt(i); + if (suggestion.equals(tileView.getData())) return tileView; + } + return null; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGridViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGridViewHolder.java new file mode 100644 index 0000000..b094260b --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGridViewHolder.java
@@ -0,0 +1,52 @@ +// 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.chrome.browser.suggestions.tile; + +import android.view.ViewGroup; + +import org.chromium.chrome.browser.suggestions.SiteSuggestion; + +import java.util.List; +/** + * A {@link SiteSectionViewHolder} specialised in displaying sites as a simple grid of tiles, + * through + * {@link TileGridLayout}. + */ +public class TileGridViewHolder extends SiteSectionViewHolder { + private final TileGridLayout mSectionView; + + public TileGridViewHolder(ViewGroup view, int maxRows, int maxColumns) { + super(view); + + mSectionView = (TileGridLayout) itemView; + mSectionView.setMaxRows(maxRows); + mSectionView.setMaxColumns(maxColumns); + } + + @Override + public void refreshData() { + assert mTileGroup.getTileSections().size() == 1; + List<Tile> tiles = mTileGroup.getTileSections().get(TileSectionType.PERSONALIZED); + assert tiles != null; + + mTileRenderer.renderTileSection(tiles, mSectionView, mTileGroup.getTileSetupDelegate()); + mTileGroup.notifyTilesRendered(); + } + + @Override + public SuggestionsTileView findTileView(SiteSuggestion data) { + return mSectionView.getTileView(data); + } + + @Override + public void bindDataSource(TileGroup tileGroup, TileRenderer tileRenderer) { + super.bindDataSource(tileGroup, tileRenderer); + } + + @Override + public void recycle() { + super.recycle(); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java new file mode 100644 index 0000000..4d0da06 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
@@ -0,0 +1,613 @@ +// 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.chrome.browser.suggestions.tile; + +import android.graphics.Bitmap; +import android.support.annotation.IntDef; +import android.support.annotation.Nullable; +import android.util.SparseArray; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnCreateContextMenuListener; + +import org.chromium.base.Callback; +import org.chromium.base.VisibleForTesting; +import org.chromium.chrome.browser.favicon.IconType; +import org.chromium.chrome.browser.favicon.LargeIconBridge; +import org.chromium.chrome.browser.native_page.ContextMenuManager; +import org.chromium.chrome.browser.native_page.ContextMenuManager.ContextMenuItemId; +import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; +import org.chromium.chrome.browser.offlinepages.OfflinePageItem; +import org.chromium.chrome.browser.suggestions.SiteSuggestion; +import org.chromium.chrome.browser.suggestions.SuggestionsConfig; +import org.chromium.chrome.browser.suggestions.SuggestionsMetrics; +import org.chromium.chrome.browser.suggestions.SuggestionsOfflineModelObserver; +import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate; +import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites; +import org.chromium.ui.mojom.WindowOpenDisposition; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * The model and controller for a group of site suggestion tiles. + */ +public class TileGroup implements MostVisitedSites.Observer { + /** + * Performs work in other parts of the system that the {@link TileGroup} should not know about. + */ + public interface Delegate { + /** + * @param tile The tile corresponding to the most visited item to remove. + * @param removalUndoneCallback The callback to invoke if the removal is reverted. The + * callback's argument is the URL being restored. + */ + void removeMostVisitedItem(Tile tile, Callback<String> removalUndoneCallback); + + void openMostVisitedItem(int windowDisposition, Tile tile); + + /** + * Gets the list of most visited sites. + * @param observer The observer to be notified with the list of sites. + * @param maxResults The maximum number of sites to retrieve. + */ + void setMostVisitedSitesObserver(MostVisitedSites.Observer observer, int maxResults); + + /** + * Called when the tile group has completely finished loading (all views will be inflated + * and any dependent resources will have been loaded). + * @param tiles The tiles owned by the {@link TileGroup}. Used to record metrics. + */ + void onLoadingComplete(List<Tile> tiles); + + /** + * To be called before this instance is abandoned to the garbage collector so it can do any + * necessary cleanups. This instance must not be used after this method is called. + */ + void destroy(); + } + + /** + * An observer for events in the {@link TileGroup}. + */ + public interface Observer { + /** + * Called when the tile group is initialised and when any of the tile data has changed, + * such as an icon, url, or title. + */ + void onTileDataChanged(); + + /** + * Called when the number of tiles has changed. + */ + void onTileCountChanged(); + + /** + * Called when a tile icon has changed. + * @param tile The tile for which the icon has changed. + */ + void onTileIconChanged(Tile tile); + + /** + * Called when the visibility of a tile's offline badge has changed. + * @param tile The tile for which the visibility of the offline badge has changed. + */ + void onTileOfflineBadgeVisibilityChanged(Tile tile); + } + + /** + * A delegate to allow {@link TileRenderer} to setup behaviours for the newly created views + * associated to a Tile. + */ + public interface TileSetupDelegate { + /** + * Returns a delegate that will handle user interactions with the view created for the tile. + */ + TileInteractionDelegate createInteractionDelegate(Tile tile); + + /** + * Returns a callback to be invoked when the icon for the provided tile is loaded. It will + * be responsible for updating the tile data and triggering the visual refresh. + */ + LargeIconBridge.LargeIconCallback createIconLoadCallback(Tile tile); + } + + /** + * Constants used to track the current operations on the group and notify the {@link Delegate} + * when the expected sequence of potentially asynchronous operations is complete. + */ + @VisibleForTesting + @IntDef({TileTask.FETCH_DATA, TileTask.SCHEDULE_ICON_FETCH, TileTask.FETCH_ICON}) + @Retention(RetentionPolicy.SOURCE) + @interface TileTask { + /** + * An event that should result in new data being loaded happened. + * Can be an asynchronous task, spanning from when the {@link Observer} is registered to + * when the initial load completes. + */ + int FETCH_DATA = 1; + + /** + * New tile data has been loaded and we are expecting the related icons to be fetched. + * Can be an asynchronous task, as we rely on it being triggered by the embedder, some time + * after {@link Observer#onTileDataChanged()} is called. + */ + int SCHEDULE_ICON_FETCH = 2; + + /** + * The icon for a tile is being fetched. + * Asynchronous task, that is started for each icon that needs to be loaded. + */ + int FETCH_ICON = 3; + } + + private final SuggestionsUiDelegate mUiDelegate; + private final ContextMenuManager mContextMenuManager; + private final Delegate mTileGroupDelegate; + private final Observer mObserver; + private final TileRenderer mTileRenderer; + + /** + * Tracks the tasks currently in flight. + * + * We only care about which ones are pending, not their order, and we can have multiple tasks + * pending of the same type. Hence exposing the type as Collection rather than List or Set. + */ + private final Collection<Integer> mPendingTasks = new ArrayList<>(); + + /** Access point to offline related features. */ + private final OfflineModelObserver mOfflineModelObserver; + + /** + * Source of truth for the tile data. Avoid keeping a reference to a tile in long running + * callbacks, as it might be thrown out before it is called. Use URL or site data to look it up + * at the right time instead. + * @see #findTile(SiteSuggestion) + * @see #findTilesForUrl(String) + */ + private SparseArray<List<Tile>> mTileSections = createEmptyTileData(); + + /** Most recently received tile data that has not been displayed yet. */ + @Nullable + private List<SiteSuggestion> mPendingTiles; + + /** + * URL of the most recently removed tile. Used to identify when a tile removal is confirmed by + * the tile backend. + */ + @Nullable + private String mPendingRemovalUrl; + + /** + * URL of the most recently added tile. Used to identify when a given tile's insertion is + * confirmed by the tile backend. This is relevant when a previously existing tile is removed, + * then the user undoes the action and wants that tile back. + */ + @Nullable + private String mPendingInsertionUrl; + + private boolean mHasReceivedData; + + // TODO(dgn): Attempt to avoid cycling dependencies with TileRenderer. Is there a better way? + private final TileSetupDelegate mTileSetupDelegate = new TileSetupDelegate() { + @Override + public TileInteractionDelegate createInteractionDelegate(Tile tile) { + return new TileInteractionDelegate(tile.getData()); + } + + @Override + public LargeIconBridge.LargeIconCallback createIconLoadCallback(Tile tile) { + // TODO(dgn): We could save on fetches by avoiding a new one when there is one pending + // for the same URL, and applying the result to all matched URLs. + boolean trackLoad = + isLoadTracked() && tile.getSectionType() == TileSectionType.PERSONALIZED; + if (trackLoad) addTask(TileTask.FETCH_ICON); + return new LargeIconCallbackImpl(tile.getData(), trackLoad); + } + }; + + /** + * @param tileRenderer Used to render icons. + * @param uiDelegate Delegate used to interact with the rest of the system. + * @param contextMenuManager Used to handle context menu invocations on the tiles. + * @param tileGroupDelegate Used for interactions with the Most Visited backend. + * @param observer Will be notified of changes to the tile data. + * @param offlinePageBridge Used to update the offline badge of the tiles. + */ + public TileGroup(TileRenderer tileRenderer, SuggestionsUiDelegate uiDelegate, + ContextMenuManager contextMenuManager, Delegate tileGroupDelegate, Observer observer, + OfflinePageBridge offlinePageBridge) { + mUiDelegate = uiDelegate; + mContextMenuManager = contextMenuManager; + mTileGroupDelegate = tileGroupDelegate; + mObserver = observer; + mTileRenderer = tileRenderer; + mOfflineModelObserver = new OfflineModelObserver(offlinePageBridge); + mUiDelegate.addDestructionObserver(mOfflineModelObserver); + } + + @Override + public void onSiteSuggestionsAvailable(List<SiteSuggestion> siteSuggestions) { + // Only transforms the incoming tiles and stores them in a buffer for when we decide to + // refresh the tiles in the UI. + + boolean removalCompleted = mPendingRemovalUrl != null; + boolean insertionCompleted = mPendingInsertionUrl == null; + + mPendingTiles = new ArrayList<>(); + for (SiteSuggestion suggestion : siteSuggestions) { + mPendingTiles.add(suggestion); + + // Only tiles in the personal section can be modified. + if (suggestion.sectionType != TileSectionType.PERSONALIZED) continue; + if (suggestion.url.equals(mPendingRemovalUrl)) removalCompleted = false; + if (suggestion.url.equals(mPendingInsertionUrl)) insertionCompleted = true; + } + + boolean expectedChangeCompleted = false; + if (mPendingRemovalUrl != null && removalCompleted) { + mPendingRemovalUrl = null; + expectedChangeCompleted = true; + } + if (mPendingInsertionUrl != null && insertionCompleted) { + mPendingInsertionUrl = null; + expectedChangeCompleted = true; + } + + if (!mHasReceivedData || !mUiDelegate.isVisible() || expectedChangeCompleted) loadTiles(); + } + + @Override + public void onIconMadeAvailable(String siteUrl) { + for (Tile tile : findTilesForUrl(siteUrl)) { + mTileRenderer.updateIcon(tile.getData(), + new LargeIconCallbackImpl(tile.getData(), /* trackLoadTask = */ false)); + } + } + + /** + * Instructs this instance to start listening for data. The {@link TileGroup.Observer} may be + * called immediately if new data is received synchronously. + * @param maxResults The maximum number of sites to retrieve. + */ + public void startObserving(int maxResults) { + addTask(TileTask.FETCH_DATA); + mTileGroupDelegate.setMostVisitedSitesObserver(this, maxResults); + } + + /** + * Method to be called when a tile render has been triggered, to let the {@link TileGroup} + * update its internal task tracking status. + * @see Delegate#onLoadingComplete(List) + */ + public void notifyTilesRendered() { + // Icon fetch scheduling was done when building the tile views. + if (isLoadTracked()) removeTask(TileTask.SCHEDULE_ICON_FETCH); + } + + /** @return the sites currently loaded in the group, grouped by vertical. */ + public SparseArray<List<Tile>> getTileSections() { + return mTileSections; + } + + public boolean hasReceivedData() { + return mHasReceivedData; + } + + /** @return Whether the group has no sites to display. */ + public boolean isEmpty() { + for (int i = 0; i < mTileSections.size(); i++) { + if (!mTileSections.valueAt(i).isEmpty()) return false; + } + return true; + } + + /** + * To be called when the view displaying the tile group becomes visible. + * @param trackLoadTask whether the delegate should be notified that the load is completed + * through {@link Delegate#onLoadingComplete(List)}. + */ + public void onSwitchToForeground(boolean trackLoadTask) { + if (trackLoadTask) addTask(TileTask.FETCH_DATA); + if (mPendingTiles != null) loadTiles(); + if (trackLoadTask) removeTask(TileTask.FETCH_DATA); + } + + /** Loads tile data from {@link #mPendingTiles} and clears it afterwards. */ + private void loadTiles() { + assert mPendingTiles != null; + + boolean isInitialLoad = !mHasReceivedData; + mHasReceivedData = true; + + boolean dataChanged = isInitialLoad; + List<Tile> personalisedTiles = mTileSections.get(TileSectionType.PERSONALIZED); + int oldPersonalisedTilesCount = personalisedTiles == null ? 0 : personalisedTiles.size(); + + SparseArray<List<Tile>> newSites = createEmptyTileData(); + for (int i = 0; i < mPendingTiles.size(); ++i) { + SiteSuggestion suggestion = mPendingTiles.get(i); + Tile tile = findTile(suggestion); + if (tile == null) { + dataChanged = true; + tile = new Tile(suggestion, i); + } + + List<Tile> sectionTiles = newSites.get(suggestion.sectionType); + if (sectionTiles == null) { + sectionTiles = new ArrayList<>(); + newSites.append(suggestion.sectionType, sectionTiles); + } + + // This is not supposed to happen but does. See https://crbug.com/703628 + if (findTile(suggestion.url, sectionTiles) != null) continue; + + sectionTiles.add(tile); + } + + mTileSections = newSites; + mPendingTiles = null; + + // TODO(dgn): change these events, maybe introduce new ones or just change semantics? This + // will depend on the UI to be implemented and the desired refresh behaviour. + List<Tile> personalizedTiles = mTileSections.get(TileSectionType.PERSONALIZED); + int numberOfPersonalizedTiles = personalizedTiles == null ? 0 : personalizedTiles.size(); + boolean countChanged = + isInitialLoad || numberOfPersonalizedTiles != oldPersonalisedTilesCount; + dataChanged = dataChanged || countChanged; + + if (!dataChanged) return; + + mOfflineModelObserver.updateAllSuggestionsOfflineAvailability( + /* reportPrefetchedSuggestionsCount = */ false); + + if (countChanged) mObserver.onTileCountChanged(); + + if (isLoadTracked()) addTask(TileTask.SCHEDULE_ICON_FETCH); + mObserver.onTileDataChanged(); + + if (isInitialLoad) removeTask(TileTask.FETCH_DATA); + } + + @Nullable + private Tile findTile(SiteSuggestion suggestion) { + if (mTileSections.get(suggestion.sectionType) == null) return null; + for (Tile tile : mTileSections.get(suggestion.sectionType)) { + if (tile.getData().equals(suggestion)) return tile; + } + return null; + } + + /** + * @param url The URL to search for. + * @param tiles The section to search in, represented by the contained list of tiles. + * @return A tile matching the provided URL and section, or {@code null} if none is found. + */ + private Tile findTile(String url, @Nullable List<Tile> tiles) { + if (tiles == null) return null; + for (Tile tile : tiles) { + if (tile.getUrl().equals(url)) return tile; + } + return null; + } + + /** @return All tiles matching the provided URL, or an empty list if none is found. */ + private List<Tile> findTilesForUrl(String url) { + List<Tile> tiles = new ArrayList<>(); + for (int i = 0; i < mTileSections.size(); ++i) { + for (Tile tile : mTileSections.valueAt(i)) { + if (tile.getUrl().equals(url)) tiles.add(tile); + } + } + return tiles; + } + + private void addTask(@TileTask int task) { + mPendingTasks.add(task); + } + + private void removeTask(@TileTask int task) { + boolean removedTask = mPendingTasks.remove(task); + assert removedTask; + + if (mPendingTasks.isEmpty()) { + // TODO(dgn): We only notify about the personal tiles because that's the only ones we + // wait for to be loaded. We also currently rely on the tile order in the returned + // array as the reported position in UMA, but this is not accurate and would be broken + // if we returned all the tiles regardless of sections. + List<Tile> personalTiles = mTileSections.get(TileSectionType.PERSONALIZED); + assert personalTiles != null; + mTileGroupDelegate.onLoadingComplete(personalTiles); + } + } + + /** + * @return Whether the current load is being tracked. Unrequested task tracking updates should + * not be sent, as it would cause calling {@link Delegate#onLoadingComplete(List)} at the + * wrong moment. + */ + private boolean isLoadTracked() { + return mPendingTasks.contains(TileTask.FETCH_DATA) + || mPendingTasks.contains(TileTask.SCHEDULE_ICON_FETCH); + } + + @VisibleForTesting + boolean isTaskPending(@TileTask int task) { + return mPendingTasks.contains(task); + } + + @VisibleForTesting + TileSetupDelegate getTileSetupDelegate() { + return mTileSetupDelegate; + } + + @Nullable + public SiteSuggestion getHomepageTileData() { + for (Tile tile : mTileSections.get(TileSectionType.PERSONALIZED)) { + if (tile.getSource() == TileSource.HOMEPAGE) { + return tile.getData(); + } + } + return null; + } + + private static SparseArray<List<Tile>> createEmptyTileData() { + SparseArray<List<Tile>> newTileData = new SparseArray<>(); + + // TODO(dgn): How do we want to handle empty states and sections that have no tiles? + // Have an empty list for now that can be rendered as-is without causing issues or too much + // state checking. We will have to decide if we want empty lists or no section at all for + // the others. + newTileData.put(TileSectionType.PERSONALIZED, new ArrayList<>()); + + return newTileData; + } + + // TODO(dgn): I would like to move that to TileRenderer, but setting the data on the tile, + // notifying the observer and updating the tasks make it awkward. + private class LargeIconCallbackImpl implements LargeIconBridge.LargeIconCallback { + private final SiteSuggestion mSiteData; + private final boolean mTrackLoadTask; + + private LargeIconCallbackImpl(SiteSuggestion suggestion, boolean trackLoadTask) { + mSiteData = suggestion; + mTrackLoadTask = trackLoadTask; + } + + @Override + public void onLargeIconAvailable(@Nullable Bitmap icon, int fallbackColor, + boolean isFallbackColorDefault, @IconType int iconType) { + Tile tile = findTile(mSiteData); + if (tile != null) { // Do nothing if the tile was removed. + tile.setIconType(iconType); + if (icon == null) { + mTileRenderer.setTileIconFromColor(tile, fallbackColor, isFallbackColorDefault); + } else { + mTileRenderer.setTileIconFromBitmap(tile, icon); + } + + mObserver.onTileIconChanged(tile); + } + + // This call needs to be made after the tiles are completely initialised, for UMA. + if (mTrackLoadTask) removeTask(TileTask.FETCH_ICON); + } + } + + /** + * Implements various listener and delegate interfaces to handle user interactions with tiles. + */ + public class TileInteractionDelegate + implements ContextMenuManager.Delegate, OnClickListener, OnCreateContextMenuListener { + private final SiteSuggestion mSuggestion; + private Runnable mOnClickRunnable; + + public TileInteractionDelegate(SiteSuggestion suggestion) { + mSuggestion = suggestion; + } + + @Override + public void onClick(View view) { + Tile tile = findTile(mSuggestion); + if (tile == null) return; + + SuggestionsMetrics.recordTileTapped(); + if (mOnClickRunnable != null) mOnClickRunnable.run(); + mTileGroupDelegate.openMostVisitedItem(WindowOpenDisposition.CURRENT_TAB, tile); + } + + @Override + public void openItem(int windowDisposition) { + Tile tile = findTile(mSuggestion); + if (tile == null) return; + + mTileGroupDelegate.openMostVisitedItem(windowDisposition, tile); + } + + @Override + public void removeItem() { + Tile tile = findTile(mSuggestion); + if (tile == null) return; + + // Note: This does not track all the removals, but will track the most recent one. If + // that removal is committed, it's good enough for change detection. + mPendingRemovalUrl = mSuggestion.url; + mTileGroupDelegate.removeMostVisitedItem(tile, url -> mPendingInsertionUrl = url); + } + + @Override + public String getUrl() { + return mSuggestion.url; + } + + @Override + public String getContextMenuTitle() { + return null; + } + + @Override + public boolean isItemSupported(@ContextMenuItemId int menuItemId) { + switch (menuItemId) { + // Personalized tiles are the only tiles that can be removed. Additionally, the + // Explore tile counts as a personalized tile but cannot be removed. + case ContextMenuItemId.REMOVE: + return mSuggestion.sectionType == TileSectionType.PERSONALIZED + && mSuggestion.source != TileSource.EXPLORE; + case ContextMenuItemId.LEARN_MORE: + return SuggestionsConfig.scrollToLoad(); + case ContextMenuItemId.OPEN_IN_INCOGNITO_TAB: + return mSuggestion.source != TileSource.EXPLORE; + default: + return true; + } + } + + @Override + public void onContextMenuCreated() {} + + @Override + public void onCreateContextMenu( + ContextMenu contextMenu, View view, ContextMenuInfo contextMenuInfo) { + mContextMenuManager.createContextMenu(contextMenu, view, this); + } + + /** + * Set a runnable for click events on the tile. This is primarily used to track interaction + * with the tile used by feature engagement purposes. + * @param clickRunnable The {@link Runnable} to be executed when tile is clicked. + */ + public void setOnClickRunnable(Runnable clickRunnable) { + mOnClickRunnable = clickRunnable; + } + } + + private class OfflineModelObserver extends SuggestionsOfflineModelObserver<Tile> { + public OfflineModelObserver(OfflinePageBridge bridge) { + super(bridge); + } + + @Override + public void onSuggestionOfflineIdChanged(Tile tile, OfflinePageItem item) { + boolean oldOfflineAvailable = tile.isOfflineAvailable(); + tile.setOfflinePageOfflineId(item == null ? null : item.getOfflineId()); + + // Only notify to update the view if there will be a visible change. + if (oldOfflineAvailable == tile.isOfflineAvailable()) return; + mObserver.onTileOfflineBadgeVisibilityChanged(tile); + } + + @Override + public Iterable<Tile> getOfflinableSuggestions() { + List<Tile> tiles = new ArrayList<>(); + for (int i = 0; i < mTileSections.size(); ++i) tiles.addAll(mTileSections.valueAt(i)); + return tiles; + } + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java new file mode 100644 index 0000000..b10542c --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java
@@ -0,0 +1,137 @@ +// 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.chrome.browser.suggestions.tile; + +import android.content.Context; + +import org.chromium.base.Callback; +import org.chromium.base.metrics.RecordUserAction; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.browser.ntp.NewTabPageUma; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.snackbar.Snackbar; +import org.chromium.chrome.browser.snackbar.SnackbarManager; +import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController; +import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory; +import org.chromium.chrome.browser.suggestions.SuggestionsMetrics; +import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate; +import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites; +import org.chromium.ui.mojom.WindowOpenDisposition; + +import java.util.List; + +/** + * Reusable implementation of {@link TileGroup.Delegate}. Performs work in parts of the system that + * the {@link TileGroup} should not know about. + */ +public class TileGroupDelegateImpl implements TileGroup.Delegate { + private final Context mContext; + private final SnackbarManager mSnackbarManager; + private final SuggestionsNavigationDelegate mNavigationDelegate; + private final MostVisitedSites mMostVisitedSites; + + private boolean mIsDestroyed; + private SnackbarController mTileRemovedSnackbarController; + + public TileGroupDelegateImpl(ChromeActivity activity, Profile profile, + SuggestionsNavigationDelegate navigationDelegate, SnackbarManager snackbarManager) { + mContext = activity; + mSnackbarManager = snackbarManager; + mNavigationDelegate = navigationDelegate; + mMostVisitedSites = + SuggestionsDependencyFactory.getInstance().createMostVisitedSites(profile); + } + + @Override + public void removeMostVisitedItem(Tile item, Callback<String> removalUndoneCallback) { + assert !mIsDestroyed; + + mMostVisitedSites.addBlacklistedUrl(item.getUrl()); + showTileRemovedSnackbar(item.getUrl(), removalUndoneCallback); + } + + @Override + public void openMostVisitedItem(int windowDisposition, Tile item) { + assert !mIsDestroyed; + + String url = item.getUrl(); + + // TODO(treib): Should we call recordOpenedMostVisitedItem here? + if (windowDisposition != WindowOpenDisposition.NEW_WINDOW) { + recordOpenedTile(item); + } + + mNavigationDelegate.navigateToSuggestionUrl(windowDisposition, url); + } + + @Override + public void setMostVisitedSitesObserver(MostVisitedSites.Observer observer, int maxResults) { + assert !mIsDestroyed; + + mMostVisitedSites.setObserver(observer, maxResults); + } + + @Override + public void onLoadingComplete(List<Tile> tiles) { + // This method is called after network calls complete. It could happen after the suggestions + // surface is destroyed. + if (mIsDestroyed) return; + + for (Tile tile : tiles) { + mMostVisitedSites.recordTileImpression(tile); + } + + mMostVisitedSites.recordPageImpression(tiles.size()); + + for (Tile tile : tiles) { + if (tile.isOfflineAvailable()) { + SuggestionsMetrics.recordTileOfflineAvailability(tile.getIndex()); + } + } + } + + @Override + public void destroy() { + assert !mIsDestroyed; + mIsDestroyed = true; + + if (mTileRemovedSnackbarController != null) { + mSnackbarManager.dismissSnackbars(mTileRemovedSnackbarController); + } + mMostVisitedSites.destroy(); + } + + private void showTileRemovedSnackbar(String url, final Callback<String> removalUndoneCallback) { + if (mTileRemovedSnackbarController == null) { + mTileRemovedSnackbarController = new SnackbarController() { + @Override + public void onDismissNoAction(Object actionData) {} + + /** Undoes the tile removal. */ + @Override + public void onAction(Object actionData) { + if (mIsDestroyed) return; + String url = (String) actionData; + removalUndoneCallback.onResult(url); + mMostVisitedSites.removeBlacklistedUrl(url); + } + }; + } + Snackbar snackbar = Snackbar.make(mContext.getString(R.string.most_visited_item_removed), + mTileRemovedSnackbarController, Snackbar.TYPE_ACTION, + Snackbar.UMA_NTP_MOST_VISITED_DELETE_UNDO) + .setAction(mContext.getString(R.string.undo), url); + mSnackbarManager.showSnackbar(snackbar); + } + + private void recordOpenedTile(Tile tile) { + NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_MOST_VISITED_TILE); + RecordUserAction.record("MobileNTPMostVisited"); + NewTabPageUma.recordExplicitUserNavigation( + tile.getUrl(), NewTabPageUma.RAPPOR_ACTION_VISITED_SUGGESTED_TILE); + mMostVisitedSites.recordOpenedMostVisitedItem(tile); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java new file mode 100644 index 0000000..ef0b0b90 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
@@ -0,0 +1,284 @@ +// 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.chrome.browser.suggestions.tile; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; +import android.support.annotation.LayoutRes; +import android.support.graphics.drawable.VectorDrawableCompat; +import android.support.v4.graphics.drawable.RoundedBitmapDrawable; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.Log; +import org.chromium.base.VisibleForTesting; +import org.chromium.base.task.AsyncTask; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge; +import org.chromium.chrome.browser.explore_sites.ExploreSitesIPH; +import org.chromium.chrome.browser.explore_sites.MostLikelyVariation; +import org.chromium.chrome.browser.favicon.IconType; +import org.chromium.chrome.browser.favicon.LargeIconBridge; +import org.chromium.chrome.browser.feature_engagement.TrackerFactory; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.suggestions.ImageFetcher; +import org.chromium.chrome.browser.suggestions.SiteSuggestion; +import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle; +import org.chromium.chrome.browser.util.ViewUtils; +import org.chromium.chrome.browser.widget.RoundedIconGenerator; +import org.chromium.components.feature_engagement.EventConstants; +import org.chromium.components.feature_engagement.Tracker; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Utility class that renders {@link Tile}s into a provided {@link ViewGroup}, creating and + * manipulating the views as needed. + */ +public class TileRenderer { + private static final String TAG = "TileRenderer"; + + private final Resources mResources; + private final ImageFetcher mImageFetcher; + private final RoundedIconGenerator mIconGenerator; + private final Resources.Theme mTheme; + + @TileStyle + private final int mStyle; + private final int mTitleLinesCount; + private final int mDesiredIconSize; + private final int mMinIconSize; + private final float mIconCornerRadius; + + @LayoutRes + private final int mLayout; + + @LayoutRes + private final int mTopSitesLayout; + + public TileRenderer( + Context context, @TileStyle int style, int titleLines, ImageFetcher imageFetcher) { + mImageFetcher = imageFetcher; + mStyle = style; + mTitleLinesCount = titleLines; + + mResources = context.getResources(); + mTheme = context.getTheme(); + mDesiredIconSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_size); + mIconCornerRadius = mResources.getDimension(R.dimen.tile_view_icon_corner_radius); + int minIconSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_min_size); + + // On ldpi devices, mDesiredIconSize could be even smaller than the global limit. + mMinIconSize = Math.min(mDesiredIconSize, minIconSize); + + mLayout = getLayout(); + mTopSitesLayout = getTopSitesLayout(); + + int iconColor = ApiCompatibilityUtils.getColor( + mResources, R.color.default_favicon_background_color); + int iconTextSize = mResources.getDimensionPixelSize(R.dimen.tile_view_icon_text_size); + mIconGenerator = new RoundedIconGenerator( + mDesiredIconSize, mDesiredIconSize, mDesiredIconSize / 2, iconColor, iconTextSize); + } + + /** + * Renders tile views in the given {@link ViewGroup}, reusing existing tile views where + * possible because view inflation and icon loading are slow. + * @param parent The layout to render the tile views into. + * @param sectionTiles Tiles to render. + * @param setupDelegate Delegate used to setup callbacks and listeners for the new views. + */ + public void renderTileSection( + List<Tile> sectionTiles, ViewGroup parent, TileGroup.TileSetupDelegate setupDelegate) { + // Map the old tile views by url so they can be reused later. + Map<SiteSuggestion, SuggestionsTileView> oldTileViews = new HashMap<>(); + int childCount = parent.getChildCount(); + for (int i = 0; i < childCount; i++) { + SuggestionsTileView tileView = (SuggestionsTileView) parent.getChildAt(i); + oldTileViews.put(tileView.getData(), tileView); + } + + // Remove all views from the layout because even if they are reused later they'll have to be + // added back in the correct order. + parent.removeAllViews(); + + for (Tile tile : sectionTiles) { + SuggestionsTileView tileView = oldTileViews.get(tile.getData()); + if (tileView == null) { + tileView = buildTileView(tile, parent, setupDelegate); + } + + parent.addView(tileView); + } + } + + /** + * Record that a tile was clicked for IPH reasons. + */ + private void recordTileClickedForIPH(String eventName) { + Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile()); + tracker.notifyEvent(eventName); + } + + /** + * Inflates a new tile view, initializes it, and loads an icon for it. + * @param tile The tile that holds the data to populate the new tile view. + * @param parentView The parent of the new tile view. + * @param setupDelegate The delegate used to setup callbacks and listeners for the new view. + * @return The new tile view. + */ + @VisibleForTesting + SuggestionsTileView buildTileView( + Tile tile, ViewGroup parentView, TileGroup.TileSetupDelegate setupDelegate) { + SuggestionsTileView tileView; + + if (tile.getSource() == TileSource.EXPLORE) { + tileView = (TopSitesTileView) LayoutInflater.from(parentView.getContext()) + .inflate(mTopSitesLayout, parentView, false); + + int iconVariation = ExploreSitesBridge.getIconVariation(); + if (iconVariation == MostLikelyVariation.ICON_ARROW) { + tile.setIcon(VectorDrawableCompat.create( + mResources, R.drawable.ic_arrow_forward_blue_24dp, mTheme)); + tile.setType(TileVisualType.ICON_REAL); + } else if (iconVariation == MostLikelyVariation.ICON_DOTS) { + tile.setIcon(VectorDrawableCompat.create( + mResources, R.drawable.ic_apps_blue_24dp, mTheme)); + tile.setType(TileVisualType.ICON_REAL); + } else if (iconVariation == MostLikelyVariation.ICON_GROUPED) { + tile.setIcon(VectorDrawableCompat.create( + mResources, R.drawable.ic_apps_blue_24dp, mTheme)); + tile.setType(TileVisualType.ICON_DEFAULT); + + // One task to load actual icon. + LargeIconBridge.LargeIconCallback bridgeCallback = + setupDelegate.createIconLoadCallback(tile); + ExploreSitesBridge.getSummaryImage(Profile.getLastUsedProfile(), mDesiredIconSize, + (Bitmap img) + -> bridgeCallback.onLargeIconAvailable( + img, Color.BLACK, false, IconType.FAVICON)); + } + } else { + tileView = (SuggestionsTileView) LayoutInflater.from(parentView.getContext()) + .inflate(mLayout, parentView, false); + } + + tileView.initialize(tile, mTitleLinesCount); + + // Note: It is important that the callbacks below don't keep a reference to the tile or + // modify them as there is no guarantee that the same tile would be used to update the view. + if (tile.getSource() != TileSource.EXPLORE) { + fetchIcon(tile.getData(), setupDelegate.createIconLoadCallback(tile)); + } + + TileGroup.TileInteractionDelegate delegate = setupDelegate.createInteractionDelegate(tile); + if (tile.getSource() == TileSource.HOMEPAGE) { + delegate.setOnClickRunnable( + () -> recordTileClickedForIPH(EventConstants.HOMEPAGE_TILE_CLICKED)); + } else if (tile.getSource() == TileSource.EXPLORE) { + delegate.setOnClickRunnable( + () -> recordTileClickedForIPH(EventConstants.EXPLORE_SITES_TILE_TAPPED)); + } + + tileView.setOnClickListener(delegate); + tileView.setOnCreateContextMenuListener(delegate); + + if (tile.getSource() == TileSource.EXPLORE) { + ExploreSitesIPH.configureIPH(tileView, Profile.getLastUsedProfile()); + } + + return tileView; + } + + private void fetchIcon( + final SiteSuggestion siteData, final LargeIconBridge.LargeIconCallback iconCallback) { + if (siteData.whitelistIconPath.isEmpty()) { + mImageFetcher.makeLargeIconRequest(siteData.url, mMinIconSize, iconCallback); + return; + } + + AsyncTask<Bitmap> task = new AsyncTask<Bitmap>() { + @Override + protected Bitmap doInBackground() { + Bitmap bitmap = BitmapFactory.decodeFile(siteData.whitelistIconPath); + if (bitmap == null) { + Log.d(TAG, "Image decoding failed: %s", siteData.whitelistIconPath); + } + return bitmap; + } + + @Override + protected void onPostExecute(Bitmap icon) { + if (icon == null) { + mImageFetcher.makeLargeIconRequest(siteData.url, mMinIconSize, iconCallback); + } else { + iconCallback.onLargeIconAvailable(icon, Color.BLACK, false, IconType.INVALID); + } + } + }; + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + public void updateIcon( + SiteSuggestion siteData, LargeIconBridge.LargeIconCallback iconCallback) { + mImageFetcher.makeLargeIconRequest(siteData.url, mMinIconSize, iconCallback); + } + + public void setTileIconFromBitmap(Tile tile, Bitmap icon) { + int radius = Math.round(mIconCornerRadius * icon.getWidth() / mDesiredIconSize); + if (tile.getSource() == TileSource.EXPLORE) { + radius = mDesiredIconSize / 2; + } + RoundedBitmapDrawable roundedIcon = ViewUtils.createRoundedBitmapDrawable(icon, radius); + roundedIcon.setAntiAlias(true); + roundedIcon.setFilterBitmap(true); + + tile.setIcon(roundedIcon); + tile.setType(TileVisualType.ICON_REAL); + } + + public void setTileIconFromColor(Tile tile, int fallbackColor, boolean isFallbackColorDefault) { + // Explore should not have generated icons. + if (tile.getSource() == TileSource.EXPLORE) { + return; + } + mIconGenerator.setBackgroundColor(fallbackColor); + Bitmap icon = mIconGenerator.generateIconForUrl(tile.getUrl()); + tile.setIcon(new BitmapDrawable(mResources, icon)); + tile.setType( + isFallbackColorDefault ? TileVisualType.ICON_DEFAULT : TileVisualType.ICON_COLOR); + } + + @LayoutRes + private int getLayout() { + switch (mStyle) { + case TileStyle.MODERN: + return R.layout.suggestions_tile_view; + case TileStyle.MODERN_CONDENSED: + return R.layout.suggestions_tile_view_condensed; + } + assert false; + return 0; + } + + @LayoutRes + private int getTopSitesLayout() { + switch (mStyle) { + case TileStyle.MODERN: + return R.layout.top_sites_tile_view; + case TileStyle.MODERN_CONDENSED: + return R.layout.top_sites_tile_view_condensed; + } + assert false; + return 0; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java index 31b6c51e..423058c1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java
@@ -10,9 +10,6 @@ import org.chromium.chrome.browser.explore_sites.ExploreSitesBridge; import org.chromium.chrome.browser.explore_sites.MostLikelyVariation; -import org.chromium.chrome.browser.suggestions.SuggestionsTileView; -import org.chromium.chrome.browser.suggestions.Tile; -import org.chromium.chrome.browser.suggestions.TileVisualType; /** * The view for a top sites tile. Displays the title of the site beneath a large icon.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java index 1abdcd5..dd5d06c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -57,8 +57,6 @@ import org.chromium.chrome.browser.device.DeviceClassManager; import org.chromium.chrome.browser.feature_engagement.TrackerFactory; import org.chromium.chrome.browser.ntp.NewTabPage; -import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper; -import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper.MenuButtonState; import org.chromium.chrome.browser.omnibox.LocationBar; import org.chromium.chrome.browser.omnibox.LocationBarPhone; import org.chromium.chrome.browser.partnercustomizations.HomepageManager; @@ -176,8 +174,6 @@ @ViewDebug.ExportedProperty(category = "chrome") private Rect mClipRect; - private OnClickListener mNewTabListener; - @ViewDebug.ExportedProperty(category = "chrome") protected boolean mUrlFocusChangeInProgress; @@ -2684,16 +2680,6 @@ mExperimentalButton.setTranslationX(0); } - @VisibleForTesting - public View getExperimentalButtonForTesting() { - return mExperimentalButton; - } - - @VisibleForTesting - public void endExperimentalButtonAnimationForTesting() { - if (mExperimentalButtonAnimator != null) mExperimentalButtonAnimator.end(); - } - private void setTabSwitcherAnimationMenuDrawable() { mTabSwitcherAnimationMenuDrawable = ApiCompatibilityUtils @@ -2702,24 +2688,6 @@ ((BitmapDrawable) mTabSwitcherAnimationMenuDrawable).setGravity(Gravity.CENTER); } - private void setTabSwitcherAnimationMenuBadgeDrawable() { - MenuButtonState buttonState = UpdateMenuItemHelper.getInstance().getUiState().buttonState; - if (buttonState == null) return; - - Drawable darkDrawable = - ApiCompatibilityUtils.getDrawable(getResources(), buttonState.darkBadgeIcon); - Drawable lightDrawable = - ApiCompatibilityUtils.getDrawable(getResources(), buttonState.lightBadgeIcon); - - mTabSwitcherAnimationMenuBadgeDarkDrawable = darkDrawable; - mTabSwitcherAnimationMenuBadgeDarkDrawable.mutate(); - ((BitmapDrawable) mTabSwitcherAnimationMenuBadgeDarkDrawable).setGravity(Gravity.CENTER); - - mTabSwitcherAnimationMenuBadgeLightDrawable = lightDrawable; - mTabSwitcherAnimationMenuBadgeLightDrawable.mutate(); - ((BitmapDrawable) mTabSwitcherAnimationMenuBadgeLightDrawable).setGravity(Gravity.CENTER); - } - /** * Custom drawable that allows sharing the NTP search box drawable between the toolbar and the * NTP. This allows animations to continue as the drawable is switched between the two owning
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd index 4c679bd..24e57db 100644 --- a/chrome/android/java/strings/android_chrome_strings.grd +++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2294,6 +2294,9 @@ <message name="IDS_MENU_ADD_TO_APPS" desc="Text to accompany icon that will navigate to a page showing a categorized view of different applications or sites"> Add to my apps </message> + <message name="IDS_ADDED_TO_APPS" desc="Text that confirms a site or app has been added to the apps screen."> + <ph name="APP_NAME">%1$s<ex>Zomato</ex></ph> added to my apps + </message> <!-- Page info popup --> <message name="IDS_PAGE_INFO_SITE_SETTINGS_BUTTON" desc="Text in the button that opens a website's Site Settings from the Page Info dialog.">
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java index 1fbd37e..0526e6bd 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/appmenu/AppMenuTest.java
@@ -24,6 +24,7 @@ import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.Feature; +import org.chromium.base.test.util.Restriction; import org.chromium.base.test.util.RetryOnFailure; import org.chromium.base.test.util.UrlUtils; import org.chromium.chrome.browser.ChromeSwitches; @@ -37,6 +38,7 @@ import org.chromium.content_public.browser.test.util.Criteria; import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.ui.test.util.UiRestriction; import java.util.concurrent.TimeoutException; @@ -232,7 +234,7 @@ @Test @SmallTest @Feature({"Browser", "Main"}) - @DisabledTest(message="https://crbug.com/970184") + @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE) public void testHideMenuOnToggleOverview() throws TimeoutException, InterruptedException { CallbackHelper overviewModeFinishedShowingCallback = new CallbackHelper(); OverviewModeBehavior.OverviewModeObserver overviewModeObserver =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java index 9fc32fe..30d29ed 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageTest.java
@@ -53,8 +53,8 @@ import org.chromium.chrome.test.util.ViewUtils; import org.chromium.chrome.test.util.browser.Features; import org.chromium.chrome.test.util.browser.RecyclerViewTestUtils; -import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites; import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule; +import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites; import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.net.test.EmbeddedTestServer;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageLoadTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageLoadTest.java index 099daa9..ae514f1 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageLoadTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageLoadTest.java
@@ -24,14 +24,14 @@ import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.favicon.IconType; import org.chromium.chrome.browser.favicon.LargeIconBridge; -import org.chromium.chrome.browser.suggestions.Tile; -import org.chromium.chrome.browser.suggestions.TileVisualType; +import org.chromium.chrome.browser.suggestions.tile.Tile; +import org.chromium.chrome.browser.suggestions.tile.TileVisualType; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeTabbedActivityTestRule; import org.chromium.chrome.test.util.NewTabPageTestUtils; -import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites; import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule; +import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites; import org.chromium.net.test.EmbeddedTestServer; import java.io.IOException;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java index a5ccc39f..345118c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -76,8 +76,8 @@ import org.chromium.chrome.test.util.RenderTestRule; import org.chromium.chrome.test.util.browser.Features; import org.chromium.chrome.test.util.browser.RecyclerViewTestUtils; -import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites; import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule; +import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites; import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.test.util.Criteria; import org.chromium.content_public.browser.test.util.CriteriaHelper;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NtpUiCaptureTestData.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NtpUiCaptureTestData.java index b6edce5..b5d78a2 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NtpUiCaptureTestData.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NtpUiCaptureTestData.java
@@ -8,7 +8,7 @@ import static org.chromium.base.test.util.UrlUtils.getTestFilePath; import static org.chromium.chrome.test.util.browser.offlinepages.FakeOfflinePageBridge.createOfflinePageItem; -import static org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites.createSiteSuggestion; +import static org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites.createSiteSuggestion; import android.graphics.Bitmap; @@ -23,9 +23,9 @@ import org.chromium.chrome.test.util.browser.offlinepages.FakeOfflinePageBridge; import org.chromium.chrome.test.util.browser.suggestions.ContentSuggestionsTestUtils; import org.chromium.chrome.test.util.browser.suggestions.DummySuggestionsEventReporter; -import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites; import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource; import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule; +import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites; import org.chromium.content_public.browser.UiThreadTaskTraits; import java.util.Arrays;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java index ad112baf..086ce3f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerViewTest.java
@@ -45,9 +45,9 @@ import org.chromium.chrome.test.util.NewTabPageTestUtils; import org.chromium.chrome.test.util.browser.Features; import org.chromium.chrome.test.util.browser.RecyclerViewTestUtils; -import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites; import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource; import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule; +import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites; import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TestTouchUtils; import org.chromium.net.test.EmbeddedTestServer;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/HomeSheetNoTilesUiCaptureTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/HomeSheetNoTilesUiCaptureTest.java index 48fc3d8..47fefa6 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/HomeSheetNoTilesUiCaptureTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/HomeSheetNoTilesUiCaptureTest.java
@@ -21,8 +21,8 @@ import org.chromium.chrome.browser.test.ScreenShooter; import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites; import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule; +import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites; import org.chromium.ui.test.util.UiRestriction; /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java deleted file mode 100644 index e520cbb8..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGridLayoutTest.java +++ /dev/null
@@ -1,332 +0,0 @@ -// 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.chrome.browser.suggestions; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.greaterThan; - -import static org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites.createSiteSuggestion; - -import android.app.Activity; -import android.content.pm.ActivityInfo; -import android.content.res.Configuration; -import android.support.annotation.Nullable; -import android.support.test.espresso.matcher.ViewMatchers; -import android.support.test.filters.MediumTest; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.ThreadUtils; -import org.chromium.base.test.util.CallbackHelper; -import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.base.test.util.DisabledTest; -import org.chromium.base.test.util.Feature; -import org.chromium.base.test.util.RetryOnFailure; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.ChromeActivity; -import org.chromium.chrome.browser.ChromeApplication; -import org.chromium.chrome.browser.ChromeFeatureList; -import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.UrlConstants; -import org.chromium.chrome.browser.ntp.NewTabPage; -import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback; -import org.chromium.chrome.browser.offlinepages.OfflinePageItem; -import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.browser.util.ViewUtils; -import org.chromium.chrome.browser.widget.displaystyle.UiConfig; -import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.chrome.test.ChromeTabbedActivityTestRule; -import org.chromium.chrome.test.util.NewTabPageTestUtils; -import org.chromium.chrome.test.util.RenderTestRule; -import org.chromium.chrome.test.util.browser.Features.DisableFeatures; -import org.chromium.chrome.test.util.browser.offlinepages.FakeOfflinePageBridge; -import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites; -import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource; -import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule; -import org.chromium.content_public.browser.test.util.Criteria; -import org.chromium.content_public.browser.test.util.CriteriaHelper; -import org.chromium.content_public.browser.test.util.TestThreadUtils; -import org.chromium.net.test.EmbeddedTestServerRule; -import org.chromium.ui.modelutil.ListObservable; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * Instrumentation tests for the {@link TileGridLayout} on the New Tab Page. - */ -@RunWith(ChromeJUnit4ClassRunner.class) -@CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE) -public class TileGridLayoutTest { - @Rule - public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); - - @Rule - public SuggestionsDependenciesRule mSuggestionsDeps = new SuggestionsDependenciesRule(); - - @Rule - public EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule(); - - @Rule - public RenderTestRule mRenderTestRule = new RenderTestRule(); - - private static final String[] FAKE_MOST_VISITED_URLS = new String[] { - "/chrome/test/data/android/navigate/one.html", - "/chrome/test/data/android/navigate/two.html", - "/chrome/test/data/android/navigate/three.html", - "/chrome/test/data/android/navigate/four.html", - "/chrome/test/data/android/navigate/five.html", - "/chrome/test/data/android/navigate/six.html", - "/chrome/test/data/android/navigate/seven.html", - "/chrome/test/data/android/navigate/eight.html", - "/chrome/test/data/android/navigate/nine.html", - }; - - private static final String[] FAKE_MOST_VISITED_TITLES = - new String[] {"ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"}; - - private final CallbackHelper mLoadCompleteHelper = new CallbackHelper(); - - @Test - @MediumTest - @Feature({"NewTabPage", "RenderTest"}) - // TODO(https://crbug.com/906151): Add new goldens and enable ExploreSites. - @DisableFeatures(ChromeFeatureList.EXPLORE_SITES) - public void testTileGridAppearance() throws Exception { - NewTabPage ntp = setUpFakeDataToShowOnNtp(FAKE_MOST_VISITED_URLS.length); - mRenderTestRule.render(getTileGridLayout(ntp), "ntp_tile_grid_layout"); - } - - @Test - //@MediumTest - @DisabledTest(message = "crbug.com/771648") - @Feature({"NewTabPage", "RenderTest"}) - public void testModernTileGridAppearance_Full() throws IOException, InterruptedException { - View tileGridLayout = renderTiles(makeSuggestions(FAKE_MOST_VISITED_URLS.length)); - - setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, mActivityTestRule.getActivity()); - mRenderTestRule.render(tileGridLayout, "modern_full_grid_portrait"); - - setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, mActivityTestRule.getActivity()); - mRenderTestRule.render(tileGridLayout, "modern_full_grid_landscape"); - - // In landscape, modern tiles should use all available space. - int tileGridMaxWidthPx = tileGridLayout.getResources().getDimensionPixelSize( - R.dimen.tile_grid_layout_max_width); - if (((FrameLayout) tileGridLayout.getParent()).getMeasuredWidth() > tileGridMaxWidthPx) { - assertThat(tileGridLayout.getMeasuredWidth(), greaterThan(tileGridMaxWidthPx)); - } - } - - @Test - //@MediumTest - @DisabledTest(message = "crbug.com/771648") - @RetryOnFailure - @Feature({"NewTabPage", "RenderTest"}) - public void testModernTileGridAppearance_Two() throws IOException, InterruptedException { - View tileGridLayout = renderTiles(makeSuggestions(2)); - - setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, mActivityTestRule.getActivity()); - mRenderTestRule.render(tileGridLayout, "modern_two_tiles_grid_portrait"); - - setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, mActivityTestRule.getActivity()); - mRenderTestRule.render(tileGridLayout, "modern_two_tiles_grid_landscape"); - } - - @Test - @MediumTest - @Feature({"NewTabPage", "RenderTest"}) - // TODO(https://crbug.com/906151): Add new goldens and enable ExploreSites. - @DisableFeatures(ChromeFeatureList.EXPLORE_SITES) - public void testTileAppearanceModern() - throws IOException, InterruptedException, TimeoutException { - List<SiteSuggestion> suggestions = makeSuggestions(2); - List<String> offlineAvailableUrls = Collections.singletonList(suggestions.get(0).url); - ViewGroup tiles = renderTiles(suggestions, offlineAvailableUrls); - - mLoadCompleteHelper.waitForCallback(0); - - mRenderTestRule.render(tiles.getChildAt(0), "tile_modern_offline"); - mRenderTestRule.render(tiles.getChildAt(1), "tile_modern"); - } - - private List<SiteSuggestion> makeSuggestions(int count) { - List<SiteSuggestion> siteSuggestions = new ArrayList<>(count); - - assertEquals(FAKE_MOST_VISITED_URLS.length, FAKE_MOST_VISITED_TITLES.length); - assertTrue(count <= FAKE_MOST_VISITED_URLS.length); - - for (int i = 0; i < count; i++) { - String url = mTestServerRule.getServer().getURL(FAKE_MOST_VISITED_URLS[i]); - siteSuggestions.add(createSiteSuggestion(FAKE_MOST_VISITED_TITLES[i], url)); - } - - return siteSuggestions; - } - - private NewTabPage setUpFakeDataToShowOnNtp(int suggestionCount) throws InterruptedException { - List<SiteSuggestion> siteSuggestions = makeSuggestions(suggestionCount); - - FakeMostVisitedSites mMostVisitedSites = new FakeMostVisitedSites(); - mMostVisitedSites.setTileSuggestions(siteSuggestions); - mSuggestionsDeps.getFactory().mostVisitedSites = mMostVisitedSites; - - mSuggestionsDeps.getFactory().suggestionsSource = new FakeSuggestionsSource(); - - mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL); - - Tab mTab = mActivityTestRule.getActivity().getActivityTab(); - NewTabPageTestUtils.waitForNtpLoaded(mTab); - - assertTrue(mTab.getNativePage() instanceof NewTabPage); - NewTabPage ntp = (NewTabPage) mTab.getNativePage(); - - org.chromium.chrome.test.util.ViewUtils.waitForView( - (ViewGroup) ntp.getView(), ViewMatchers.withId(R.id.tile_grid_layout)); - - return ntp; - } - - private void setOrientation(final int requestedOrientation, final Activity activity) { - if (orientationMatchesRequest(activity, requestedOrientation)) return; - - TestThreadUtils.runOnUiThreadBlocking( - () -> activity.setRequestedOrientation(requestedOrientation)); - - CriteriaHelper.pollUiThread(new Criteria() { - @Override - public boolean isSatisfied() { - return orientationMatchesRequest(activity, requestedOrientation); - } - }); - } - - /** - * Checks whether the requested orientation matches the current one. - * @param activity Activity to check the orientation from. We pull its {@link Configuration} and - * content {@link View}. - * @param requestedOrientation The requested orientation, as used in - * {@link ActivityInfo#screenOrientation}. - */ - private boolean orientationMatchesRequest(Activity activity, int requestedOrientation) { - // Note: Requests use a constant from ActivityInfo, not Configuration.ORIENTATION_*! - boolean expectLandscape = requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - - // We check the orientation by looking at the dimensions of the content view. Looking at - // orientation from the configuration is not reliable as sometimes the activity gets the - // event that its configuration changed, but has not updated its layout yet. - Configuration configuration = activity.getResources().getConfiguration(); - View contentView = activity.findViewById(android.R.id.content); - int smallestWidthPx = ViewUtils.dpToPx(activity, configuration.smallestScreenWidthDp); - boolean viewIsLandscape = contentView.getMeasuredWidth() > smallestWidthPx; - - return expectLandscape == viewIsLandscape; - } - - private TileGridLayout getTileGridLayout(NewTabPage ntp) { - TileGridLayout tileGridLayout = ntp.getView().findViewById(R.id.tile_grid_layout); - assertNotNull("Unable to retrieve the TileGridLayout.", tileGridLayout); - return tileGridLayout; - } - - /** - * Starts and sets up an activity to render the provided site suggestions in the activity. - * @return the layout in which the suggestions are rendered. - */ - private TileGridLayout renderTiles(List<SiteSuggestion> siteSuggestions, - List<String> offlineUrls) throws IOException, InterruptedException { - // Launching the activity, that should now use the right UI. - mActivityTestRule.startMainActivityOnBlankPage(); - ChromeActivity activity = mActivityTestRule.getActivity(); - - // Setting up the dummy data. - FakeMostVisitedSites mostVisitedSites = new FakeMostVisitedSites(); - mostVisitedSites.setTileSuggestions(siteSuggestions); - mSuggestionsDeps.getFactory().mostVisitedSites = mostVisitedSites; - mSuggestionsDeps.getFactory().suggestionsSource = new FakeSuggestionsSource(); - - FrameLayout contentView = new FrameLayout(activity); - UiConfig uiConfig = new UiConfig(contentView); - - return TestThreadUtils.runOnUiThreadBlockingNoException(() -> { - activity.setContentView(contentView); - - SiteSectionViewHolder viewHolder = SiteSection.createViewHolder( - SiteSection.inflateSiteSection(contentView), uiConfig); - - uiConfig.updateDisplayStyle(); - - SiteSection siteSection = createSiteSection(viewHolder, uiConfig, offlineUrls); - siteSection.getTileGroupForTesting().onSwitchToForeground(false); - assertTrue("Tile Data should be visible.", siteSection.isVisible()); - - siteSection.onBindViewHolder(viewHolder, 0); - contentView.addView(viewHolder.itemView); - - return (TileGridLayout) viewHolder.itemView; - }); - } - - private TileGridLayout renderTiles(List<SiteSuggestion> siteSuggestions) - throws IOException, InterruptedException { - return renderTiles(siteSuggestions, Collections.emptyList()); - } - - private SiteSection createSiteSection( - final SiteSectionViewHolder viewHolder, UiConfig uiConfig, List<String> offlineUrls) { - ThreadUtils.assertOnUiThread(); - - ChromeActivity activity = mActivityTestRule.getActivity(); - - Profile profile = Profile.getLastUsedProfile(); - SuggestionsUiDelegate uiDelegate = new SuggestionsUiDelegateImpl( - mSuggestionsDeps.getFactory().createSuggestionSource(null), - mSuggestionsDeps.getFactory().createEventReporter(), null, profile, null, - ChromeApplication.getReferencePool(), activity.getSnackbarManager()); - - FakeOfflinePageBridge offlinePageBridge = new FakeOfflinePageBridge(); - List<OfflinePageItem> offlinePageItems = new ArrayList<>(); - for (int i = 0; i < offlineUrls.size(); i++) { - offlinePageItems.add( - FakeOfflinePageBridge.createOfflinePageItem(offlineUrls.get(i), i + 1L)); - } - offlinePageBridge.setItems(offlinePageItems); - offlinePageBridge.setIsOfflinePageModelLoaded(true); - - TileGroup.Delegate delegate = new TileGroupDelegateImpl(activity, profile, null, null) { - @Override - public void onLoadingComplete(List<Tile> tiles) { - super.onLoadingComplete(tiles); - mLoadCompleteHelper.notifyCalled(); - } - }; - - SiteSection siteSection = - new SiteSection(uiDelegate, null, delegate, offlinePageBridge, uiConfig); - - siteSection.addObserver(new ListObservable.ListObserver<PartialBindCallback>() { - @Override - public void onItemRangeChanged(ListObservable child, int index, int count, - @Nullable PartialBindCallback payload) { - if (payload != null) payload.onResult(viewHolder); - } - }); - - return siteSection; - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java deleted file mode 100644 index 634ce4a..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java +++ /dev/null
@@ -1,279 +0,0 @@ -// 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.chrome.browser.suggestions; - -import android.support.test.InstrumentationRegistry; -import android.support.test.espresso.matcher.ViewMatchers; -import android.support.test.filters.MediumTest; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.util.CallbackHelper; -import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.base.test.util.Feature; -import org.chromium.base.test.util.RetryOnFailure; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.ChromeActivity; -import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.UrlConstants; -import org.chromium.chrome.browser.native_page.ContextMenuManager; -import org.chromium.chrome.browser.ntp.NewTabPage; -import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView; -import org.chromium.chrome.browser.snackbar.SnackbarManager; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.chrome.test.ChromeTabbedActivityTestRule; -import org.chromium.chrome.test.util.NewTabPageTestUtils; -import org.chromium.chrome.test.util.ViewUtils; -import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites; -import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource; -import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule; -import org.chromium.content_public.browser.test.util.Criteria; -import org.chromium.content_public.browser.test.util.CriteriaHelper; -import org.chromium.content_public.browser.test.util.TestThreadUtils; -import org.chromium.content_public.browser.test.util.TestTouchUtils; -import org.chromium.net.test.EmbeddedTestServer; - -import java.util.ArrayList; -import java.util.Date; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -/** - * Instrumentation tests for {@link TileGroup} on the New Tab Page. - */ -@RunWith(ChromeJUnit4ClassRunner.class) -@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) -@RetryOnFailure -public class TileGroupTest { - @Rule - public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); - - @Rule - public SuggestionsDependenciesRule mSuggestionsDeps = new SuggestionsDependenciesRule(); - - private static final String[] FAKE_MOST_VISITED_URLS = - new String[] {"/chrome/test/data/android/navigate/one.html", - "/chrome/test/data/android/navigate/two.html", - "/chrome/test/data/android/navigate/three.html"}; - - private NewTabPage mNtp; - private String[] mSiteSuggestionUrls; - private FakeMostVisitedSites mMostVisitedSites; - private EmbeddedTestServer mTestServer; - - @Before - public void setUp() throws Exception { - mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext()); - - mSiteSuggestionUrls = mTestServer.getURLs(FAKE_MOST_VISITED_URLS); - - mMostVisitedSites = new FakeMostVisitedSites(); - mSuggestionsDeps.getFactory().mostVisitedSites = mMostVisitedSites; - mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls); - - mSuggestionsDeps.getFactory().suggestionsSource = new FakeSuggestionsSource(); - initializeTab(); - } - - public void initializeTab() throws Exception { - mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL); - Tab mTab = mActivityTestRule.getActivity().getActivityTab(); - NewTabPageTestUtils.waitForNtpLoaded(mTab); - - Assert.assertTrue(mTab.getNativePage() instanceof NewTabPage); - mNtp = (NewTabPage) mTab.getNativePage(); - - ViewUtils.waitForView( - (ViewGroup) mNtp.getView(), ViewMatchers.withId(R.id.tile_grid_layout)); - } - - @After - public void tearDown() throws Exception { - mTestServer.stopAndDestroyServer(); - - } - - @Test - @MediumTest - @Feature({"NewTabPage"}) - public void testDismissTileWithContextMenu() throws Exception { - SiteSuggestion siteToDismiss = mMostVisitedSites.getCurrentSites().get(0); - final View tileView = getTileViewFor(siteToDismiss); - - // Dismiss the tile using the context menu. - invokeContextMenu(tileView, ContextMenuManager.ContextMenuItemId.REMOVE); - Assert.assertTrue(mMostVisitedSites.isUrlBlacklisted(mSiteSuggestionUrls[0])); - - // Ensure that the removal is reflected in the ui. - Assert.assertEquals(3, getTileGridLayout().getChildCount()); - TestThreadUtils.runOnUiThreadBlocking(() -> { - mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls[1], mSiteSuggestionUrls[2]); - }); - waitForTileRemoved(siteToDismiss); - Assert.assertEquals(2, getTileGridLayout().getChildCount()); - } - - @Test - @MediumTest - @Feature({"NewTabPage"}) - public void testDismissExploreTileWithContextMenuFails() throws Exception { - SiteSuggestion exploreTile = recreateSuggestionsWithExploreTile(); - initializeTab(); - - Assert.assertEquals(4, getTileGridLayout().getChildCount()); - - final View tileView = getTileViewFor(exploreTile); - TestTouchUtils.performLongClickOnMainSync( - InstrumentationRegistry.getInstrumentation(), tileView); - Assert.assertFalse(InstrumentationRegistry.getInstrumentation().invokeContextMenuAction( - mActivityTestRule.getActivity(), ContextMenuManager.ContextMenuItemId.REMOVE, 0)); - Assert.assertEquals(4, getTileGridLayout().getChildCount()); - } - - @Test - @MediumTest - @Feature({"NewTabPage"}) - public void testDismissTileUndo() throws Exception { - SiteSuggestion siteToDismiss = mMostVisitedSites.getCurrentSites().get(0); - final ViewGroup tileContainer = getTileGridLayout(); - final View tileView = getTileViewFor(siteToDismiss); - Assert.assertEquals(3, tileContainer.getChildCount()); - - // Dismiss the tile using the context menu. - invokeContextMenu(tileView, ContextMenuManager.ContextMenuItemId.REMOVE); - - // Ensure that the removal update goes through. - TestThreadUtils.runOnUiThreadBlocking(() -> { - mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls[1], mSiteSuggestionUrls[2]); - }); - waitForTileRemoved(siteToDismiss); - Assert.assertEquals(2, tileContainer.getChildCount()); - final View snackbarButton = waitForSnackbar(mActivityTestRule.getActivity()); - - Assert.assertTrue(mMostVisitedSites.isUrlBlacklisted(mSiteSuggestionUrls[0])); - TestThreadUtils.runOnUiThreadBlocking(() -> { snackbarButton.callOnClick(); }); - - Assert.assertFalse(mMostVisitedSites.isUrlBlacklisted(mSiteSuggestionUrls[0])); - - // Ensure that the removal of the update goes through. - TestThreadUtils.runOnUiThreadBlocking( - () -> { mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls); }); - waitForTileAdded(siteToDismiss); - Assert.assertEquals(3, tileContainer.getChildCount()); - } - - private NewTabPageRecyclerView getRecyclerView() { - return mNtp.getNewTabPageView().getRecyclerView(); - } - - private TileGridLayout getTileGridLayout() { - ViewGroup newTabPageLayout = mNtp.getNewTabPageLayout(); - Assert.assertNotNull("Unable to retrieve the NewTabPageLayout.", newTabPageLayout); - - TileGridLayout tileGridLayout = newTabPageLayout.findViewById(R.id.tile_grid_layout); - Assert.assertNotNull("Unable to retrieve the TileGridLayout.", tileGridLayout); - return tileGridLayout; - } - - private View getTileViewFor(SiteSuggestion suggestion) { - View tileView = getTileGridLayout().getTileView(suggestion); - Assert.assertNotNull("Tile not found for suggestion " + suggestion.url, tileView); - - return tileView; - } - - private void invokeContextMenu(View view, int contextMenuItemId) throws ExecutionException { - TestTouchUtils.performLongClickOnMainSync( - InstrumentationRegistry.getInstrumentation(), view); - Assert.assertTrue(InstrumentationRegistry.getInstrumentation().invokeContextMenuAction( - mActivityTestRule.getActivity(), contextMenuItemId, 0)); - } - - /** Wait for the snackbar associated to a tile dismissal to be shown and returns its button. */ - private static View waitForSnackbar(final ChromeActivity activity) { - final String expectedSnackbarMessage = - activity.getResources().getString(R.string.most_visited_item_removed); - CriteriaHelper.pollUiThread(new Criteria("The snackbar was not shown.") { - @Override - public boolean isSatisfied() { - SnackbarManager snackbarManager = activity.getSnackbarManager(); - if (!snackbarManager.isShowing()) return false; - - TextView snackbarMessage = (TextView) activity.findViewById(R.id.snackbar_message); - if (snackbarMessage == null) return false; - - return snackbarMessage.getText().toString().equals(expectedSnackbarMessage); - } - }); - - return activity.findViewById(R.id.snackbar_button); - } - - private void waitForTileRemoved(final SiteSuggestion suggestion) - throws TimeoutException, InterruptedException { - TileGridLayout tileContainer = getTileGridLayout(); - final SuggestionsTileView removedTile = tileContainer.getTileView(suggestion); - if (removedTile == null) return; - - final CallbackHelper callback = new CallbackHelper(); - tileContainer.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() { - @Override - public void onChildViewAdded(View parent, View child) {} - - @Override - public void onChildViewRemoved(View parent, View child) { - if (child == removedTile) callback.notifyCalled(); - } - }); - callback.waitForCallback("The expected tile was not removed.", 0); - tileContainer.setOnHierarchyChangeListener(null); - } - - private void waitForTileAdded(final SiteSuggestion suggestion) - throws TimeoutException, InterruptedException { - TileGridLayout tileContainer = getTileGridLayout(); - if (tileContainer.getTileView(suggestion) != null) return; - - final CallbackHelper callback = new CallbackHelper(); - tileContainer.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() { - @Override - public void onChildViewAdded(View parent, View child) { - if (!(child instanceof SuggestionsTileView)) return; - if (!((SuggestionsTileView) child).getData().equals(suggestion)) return; - - callback.notifyCalled(); - } - - @Override - public void onChildViewRemoved(View parent, View child) {} - }); - callback.waitForCallback("The expected tile was not added.", 0); - tileContainer.setOnHierarchyChangeListener(null); - } - - private SiteSuggestion recreateSuggestionsWithExploreTile() { - // need a copy of the list in order to modify it. - final ArrayList<SiteSuggestion> currentSuggestions = - new ArrayList<>(mMostVisitedSites.getCurrentSites()); - - SiteSuggestion exploreTile = new SiteSuggestion("chrome-native://explore", - "chrome-native://explore", "", TileTitleSource.UNKNOWN, TileSource.EXPLORE, - TileSectionType.PERSONALIZED, new Date()); - currentSuggestions.add(exploreTile); - TestThreadUtils.runOnUiThreadBlocking( - () -> mMostVisitedSites.setTileSuggestions(currentSuggestions)); - - return exploreTile; - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java new file mode 100644 index 0000000..81ba1615 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGridLayoutTest.java
@@ -0,0 +1,335 @@ +// 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.chrome.browser.suggestions.tile; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; + +import static org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites.createSiteSuggestion; + +import android.app.Activity; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.support.annotation.Nullable; +import android.support.test.espresso.matcher.ViewMatchers; +import android.support.test.filters.MediumTest; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.ThreadUtils; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisabledTest; +import org.chromium.base.test.util.Feature; +import org.chromium.base.test.util.RetryOnFailure; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.browser.ChromeApplication; +import org.chromium.chrome.browser.ChromeFeatureList; +import org.chromium.chrome.browser.ChromeSwitches; +import org.chromium.chrome.browser.UrlConstants; +import org.chromium.chrome.browser.ntp.NewTabPage; +import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback; +import org.chromium.chrome.browser.offlinepages.OfflinePageItem; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.suggestions.SiteSuggestion; +import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate; +import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.util.ViewUtils; +import org.chromium.chrome.browser.widget.displaystyle.UiConfig; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.chrome.test.util.NewTabPageTestUtils; +import org.chromium.chrome.test.util.RenderTestRule; +import org.chromium.chrome.test.util.browser.Features.DisableFeatures; +import org.chromium.chrome.test.util.browser.offlinepages.FakeOfflinePageBridge; +import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource; +import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule; +import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites; +import org.chromium.content_public.browser.test.util.Criteria; +import org.chromium.content_public.browser.test.util.CriteriaHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.net.test.EmbeddedTestServerRule; +import org.chromium.ui.modelutil.ListObservable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeoutException; + +/** + * Instrumentation tests for the {@link TileGridLayout} on the New Tab Page. + */ +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE) +public class TileGridLayoutTest { + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + + @Rule + public SuggestionsDependenciesRule mSuggestionsDeps = new SuggestionsDependenciesRule(); + + @Rule + public EmbeddedTestServerRule mTestServerRule = new EmbeddedTestServerRule(); + + @Rule + public RenderTestRule mRenderTestRule = new RenderTestRule(); + + private static final String[] FAKE_MOST_VISITED_URLS = new String[] { + "/chrome/test/data/android/navigate/one.html", + "/chrome/test/data/android/navigate/two.html", + "/chrome/test/data/android/navigate/three.html", + "/chrome/test/data/android/navigate/four.html", + "/chrome/test/data/android/navigate/five.html", + "/chrome/test/data/android/navigate/six.html", + "/chrome/test/data/android/navigate/seven.html", + "/chrome/test/data/android/navigate/eight.html", + "/chrome/test/data/android/navigate/nine.html", + }; + + private static final String[] FAKE_MOST_VISITED_TITLES = + new String[] {"ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"}; + + private final CallbackHelper mLoadCompleteHelper = new CallbackHelper(); + + @Test + @MediumTest + @Feature({"NewTabPage", "RenderTest"}) + // TODO(https://crbug.com/906151): Add new goldens and enable ExploreSites. + @DisableFeatures(ChromeFeatureList.EXPLORE_SITES) + public void testTileGridAppearance() throws Exception { + NewTabPage ntp = setUpFakeDataToShowOnNtp(FAKE_MOST_VISITED_URLS.length); + mRenderTestRule.render(getTileGridLayout(ntp), "ntp_tile_grid_layout"); + } + + @Test + //@MediumTest + @DisabledTest(message = "crbug.com/771648") + @Feature({"NewTabPage", "RenderTest"}) + public void testModernTileGridAppearance_Full() throws IOException, InterruptedException { + View tileGridLayout = renderTiles(makeSuggestions(FAKE_MOST_VISITED_URLS.length)); + + setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, mActivityTestRule.getActivity()); + mRenderTestRule.render(tileGridLayout, "modern_full_grid_portrait"); + + setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, mActivityTestRule.getActivity()); + mRenderTestRule.render(tileGridLayout, "modern_full_grid_landscape"); + + // In landscape, modern tiles should use all available space. + int tileGridMaxWidthPx = tileGridLayout.getResources().getDimensionPixelSize( + R.dimen.tile_grid_layout_max_width); + if (((FrameLayout) tileGridLayout.getParent()).getMeasuredWidth() > tileGridMaxWidthPx) { + assertThat(tileGridLayout.getMeasuredWidth(), greaterThan(tileGridMaxWidthPx)); + } + } + + @Test + //@MediumTest + @DisabledTest(message = "crbug.com/771648") + @RetryOnFailure + @Feature({"NewTabPage", "RenderTest"}) + public void testModernTileGridAppearance_Two() throws IOException, InterruptedException { + View tileGridLayout = renderTiles(makeSuggestions(2)); + + setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, mActivityTestRule.getActivity()); + mRenderTestRule.render(tileGridLayout, "modern_two_tiles_grid_portrait"); + + setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, mActivityTestRule.getActivity()); + mRenderTestRule.render(tileGridLayout, "modern_two_tiles_grid_landscape"); + } + + @Test + @MediumTest + @Feature({"NewTabPage", "RenderTest"}) + // TODO(https://crbug.com/906151): Add new goldens and enable ExploreSites. + @DisableFeatures(ChromeFeatureList.EXPLORE_SITES) + public void testTileAppearanceModern() + throws IOException, InterruptedException, TimeoutException { + List<SiteSuggestion> suggestions = makeSuggestions(2); + List<String> offlineAvailableUrls = Collections.singletonList(suggestions.get(0).url); + ViewGroup tiles = renderTiles(suggestions, offlineAvailableUrls); + + mLoadCompleteHelper.waitForCallback(0); + + mRenderTestRule.render(tiles.getChildAt(0), "tile_modern_offline"); + mRenderTestRule.render(tiles.getChildAt(1), "tile_modern"); + } + + private List<SiteSuggestion> makeSuggestions(int count) { + List<SiteSuggestion> siteSuggestions = new ArrayList<>(count); + + assertEquals(FAKE_MOST_VISITED_URLS.length, FAKE_MOST_VISITED_TITLES.length); + assertTrue(count <= FAKE_MOST_VISITED_URLS.length); + + for (int i = 0; i < count; i++) { + String url = mTestServerRule.getServer().getURL(FAKE_MOST_VISITED_URLS[i]); + siteSuggestions.add(createSiteSuggestion(FAKE_MOST_VISITED_TITLES[i], url)); + } + + return siteSuggestions; + } + + private NewTabPage setUpFakeDataToShowOnNtp(int suggestionCount) throws InterruptedException { + List<SiteSuggestion> siteSuggestions = makeSuggestions(suggestionCount); + + FakeMostVisitedSites mMostVisitedSites = new FakeMostVisitedSites(); + mMostVisitedSites.setTileSuggestions(siteSuggestions); + mSuggestionsDeps.getFactory().mostVisitedSites = mMostVisitedSites; + + mSuggestionsDeps.getFactory().suggestionsSource = new FakeSuggestionsSource(); + + mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL); + + Tab mTab = mActivityTestRule.getActivity().getActivityTab(); + NewTabPageTestUtils.waitForNtpLoaded(mTab); + + assertTrue(mTab.getNativePage() instanceof NewTabPage); + NewTabPage ntp = (NewTabPage) mTab.getNativePage(); + + org.chromium.chrome.test.util.ViewUtils.waitForView( + (ViewGroup) ntp.getView(), ViewMatchers.withId(R.id.tile_grid_layout)); + + return ntp; + } + + private void setOrientation(final int requestedOrientation, final Activity activity) { + if (orientationMatchesRequest(activity, requestedOrientation)) return; + + TestThreadUtils.runOnUiThreadBlocking( + () -> activity.setRequestedOrientation(requestedOrientation)); + + CriteriaHelper.pollUiThread(new Criteria() { + @Override + public boolean isSatisfied() { + return orientationMatchesRequest(activity, requestedOrientation); + } + }); + } + + /** + * Checks whether the requested orientation matches the current one. + * @param activity Activity to check the orientation from. We pull its {@link Configuration} and + * content {@link View}. + * @param requestedOrientation The requested orientation, as used in + * {@link ActivityInfo#screenOrientation}. + */ + private boolean orientationMatchesRequest(Activity activity, int requestedOrientation) { + // Note: Requests use a constant from ActivityInfo, not Configuration.ORIENTATION_*! + boolean expectLandscape = requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + + // We check the orientation by looking at the dimensions of the content view. Looking at + // orientation from the configuration is not reliable as sometimes the activity gets the + // event that its configuration changed, but has not updated its layout yet. + Configuration configuration = activity.getResources().getConfiguration(); + View contentView = activity.findViewById(android.R.id.content); + int smallestWidthPx = ViewUtils.dpToPx(activity, configuration.smallestScreenWidthDp); + boolean viewIsLandscape = contentView.getMeasuredWidth() > smallestWidthPx; + + return expectLandscape == viewIsLandscape; + } + + private TileGridLayout getTileGridLayout(NewTabPage ntp) { + TileGridLayout tileGridLayout = ntp.getView().findViewById(R.id.tile_grid_layout); + assertNotNull("Unable to retrieve the TileGridLayout.", tileGridLayout); + return tileGridLayout; + } + + /** + * Starts and sets up an activity to render the provided site suggestions in the activity. + * @return the layout in which the suggestions are rendered. + */ + private TileGridLayout renderTiles(List<SiteSuggestion> siteSuggestions, + List<String> offlineUrls) throws IOException, InterruptedException { + // Launching the activity, that should now use the right UI. + mActivityTestRule.startMainActivityOnBlankPage(); + ChromeActivity activity = mActivityTestRule.getActivity(); + + // Setting up the dummy data. + FakeMostVisitedSites mostVisitedSites = new FakeMostVisitedSites(); + mostVisitedSites.setTileSuggestions(siteSuggestions); + mSuggestionsDeps.getFactory().mostVisitedSites = mostVisitedSites; + mSuggestionsDeps.getFactory().suggestionsSource = new FakeSuggestionsSource(); + + FrameLayout contentView = new FrameLayout(activity); + UiConfig uiConfig = new UiConfig(contentView); + + return TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + activity.setContentView(contentView); + + SiteSectionViewHolder viewHolder = SiteSection.createViewHolder( + SiteSection.inflateSiteSection(contentView), uiConfig); + + uiConfig.updateDisplayStyle(); + + SiteSection siteSection = createSiteSection(viewHolder, uiConfig, offlineUrls); + siteSection.getTileGroupForTesting().onSwitchToForeground(false); + assertTrue("Tile Data should be visible.", siteSection.isVisible()); + + siteSection.onBindViewHolder(viewHolder, 0); + contentView.addView(viewHolder.itemView); + + return (TileGridLayout) viewHolder.itemView; + }); + } + + private TileGridLayout renderTiles(List<SiteSuggestion> siteSuggestions) + throws IOException, InterruptedException { + return renderTiles(siteSuggestions, Collections.emptyList()); + } + + private SiteSection createSiteSection( + final SiteSectionViewHolder viewHolder, UiConfig uiConfig, List<String> offlineUrls) { + ThreadUtils.assertOnUiThread(); + + ChromeActivity activity = mActivityTestRule.getActivity(); + + Profile profile = Profile.getLastUsedProfile(); + SuggestionsUiDelegate uiDelegate = new SuggestionsUiDelegateImpl( + mSuggestionsDeps.getFactory().createSuggestionSource(null), + mSuggestionsDeps.getFactory().createEventReporter(), null, profile, null, + ChromeApplication.getReferencePool(), activity.getSnackbarManager()); + + FakeOfflinePageBridge offlinePageBridge = new FakeOfflinePageBridge(); + List<OfflinePageItem> offlinePageItems = new ArrayList<>(); + for (int i = 0; i < offlineUrls.size(); i++) { + offlinePageItems.add( + FakeOfflinePageBridge.createOfflinePageItem(offlineUrls.get(i), i + 1L)); + } + offlinePageBridge.setItems(offlinePageItems); + offlinePageBridge.setIsOfflinePageModelLoaded(true); + + TileGroup.Delegate delegate = new TileGroupDelegateImpl(activity, profile, null, null) { + @Override + public void onLoadingComplete(List<Tile> tiles) { + super.onLoadingComplete(tiles); + mLoadCompleteHelper.notifyCalled(); + } + }; + + SiteSection siteSection = + new SiteSection(uiDelegate, null, delegate, offlinePageBridge, uiConfig); + + siteSection.addObserver(new ListObservable.ListObserver<PartialBindCallback>() { + @Override + public void onItemRangeChanged(ListObservable child, int index, int count, + @Nullable PartialBindCallback payload) { + if (payload != null) payload.onResult(viewHolder); + } + }); + + return siteSection; + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupTest.java new file mode 100644 index 0000000..22d3713 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupTest.java
@@ -0,0 +1,279 @@ +// 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.chrome.browser.suggestions.tile; + +import android.support.test.InstrumentationRegistry; +import android.support.test.espresso.matcher.ViewMatchers; +import android.support.test.filters.MediumTest; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Feature; +import org.chromium.base.test.util.RetryOnFailure; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.browser.ChromeSwitches; +import org.chromium.chrome.browser.UrlConstants; +import org.chromium.chrome.browser.native_page.ContextMenuManager; +import org.chromium.chrome.browser.ntp.NewTabPage; +import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView; +import org.chromium.chrome.browser.snackbar.SnackbarManager; +import org.chromium.chrome.browser.suggestions.SiteSuggestion; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.chrome.test.util.NewTabPageTestUtils; +import org.chromium.chrome.test.util.ViewUtils; +import org.chromium.chrome.test.util.browser.suggestions.FakeSuggestionsSource; +import org.chromium.chrome.test.util.browser.suggestions.SuggestionsDependenciesRule; +import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites; +import org.chromium.content_public.browser.test.util.Criteria; +import org.chromium.content_public.browser.test.util.CriteriaHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.content_public.browser.test.util.TestTouchUtils; +import org.chromium.net.test.EmbeddedTestServer; + +import java.util.ArrayList; +import java.util.Date; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +/** + * Instrumentation tests for {@link TileGroup} on the New Tab Page. + */ +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +@RetryOnFailure +public class TileGroupTest { + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + + @Rule + public SuggestionsDependenciesRule mSuggestionsDeps = new SuggestionsDependenciesRule(); + + private static final String[] FAKE_MOST_VISITED_URLS = + new String[] {"/chrome/test/data/android/navigate/one.html", + "/chrome/test/data/android/navigate/two.html", + "/chrome/test/data/android/navigate/three.html"}; + + private NewTabPage mNtp; + private String[] mSiteSuggestionUrls; + private FakeMostVisitedSites mMostVisitedSites; + private EmbeddedTestServer mTestServer; + + @Before + public void setUp() throws Exception { + mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext()); + + mSiteSuggestionUrls = mTestServer.getURLs(FAKE_MOST_VISITED_URLS); + + mMostVisitedSites = new FakeMostVisitedSites(); + mSuggestionsDeps.getFactory().mostVisitedSites = mMostVisitedSites; + mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls); + + mSuggestionsDeps.getFactory().suggestionsSource = new FakeSuggestionsSource(); + initializeTab(); + } + + public void initializeTab() throws Exception { + mActivityTestRule.startMainActivityWithURL(UrlConstants.NTP_URL); + Tab mTab = mActivityTestRule.getActivity().getActivityTab(); + NewTabPageTestUtils.waitForNtpLoaded(mTab); + + Assert.assertTrue(mTab.getNativePage() instanceof NewTabPage); + mNtp = (NewTabPage) mTab.getNativePage(); + + ViewUtils.waitForView( + (ViewGroup) mNtp.getView(), ViewMatchers.withId(R.id.tile_grid_layout)); + } + + @After + public void tearDown() throws Exception { + mTestServer.stopAndDestroyServer(); + } + + @Test + @MediumTest + @Feature({"NewTabPage"}) + public void testDismissTileWithContextMenu() throws Exception { + SiteSuggestion siteToDismiss = mMostVisitedSites.getCurrentSites().get(0); + final View tileView = getTileViewFor(siteToDismiss); + + // Dismiss the tile using the context menu. + invokeContextMenu(tileView, ContextMenuManager.ContextMenuItemId.REMOVE); + Assert.assertTrue(mMostVisitedSites.isUrlBlacklisted(mSiteSuggestionUrls[0])); + + // Ensure that the removal is reflected in the ui. + Assert.assertEquals(3, getTileGridLayout().getChildCount()); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls[1], mSiteSuggestionUrls[2]); + }); + waitForTileRemoved(siteToDismiss); + Assert.assertEquals(2, getTileGridLayout().getChildCount()); + } + + @Test + @MediumTest + @Feature({"NewTabPage"}) + public void testDismissExploreTileWithContextMenuFails() throws Exception { + SiteSuggestion exploreTile = recreateSuggestionsWithExploreTile(); + initializeTab(); + + Assert.assertEquals(4, getTileGridLayout().getChildCount()); + + final View tileView = getTileViewFor(exploreTile); + TestTouchUtils.performLongClickOnMainSync( + InstrumentationRegistry.getInstrumentation(), tileView); + Assert.assertFalse(InstrumentationRegistry.getInstrumentation().invokeContextMenuAction( + mActivityTestRule.getActivity(), ContextMenuManager.ContextMenuItemId.REMOVE, 0)); + Assert.assertEquals(4, getTileGridLayout().getChildCount()); + } + + @Test + @MediumTest + @Feature({"NewTabPage"}) + public void testDismissTileUndo() throws Exception { + SiteSuggestion siteToDismiss = mMostVisitedSites.getCurrentSites().get(0); + final ViewGroup tileContainer = getTileGridLayout(); + final View tileView = getTileViewFor(siteToDismiss); + Assert.assertEquals(3, tileContainer.getChildCount()); + + // Dismiss the tile using the context menu. + invokeContextMenu(tileView, ContextMenuManager.ContextMenuItemId.REMOVE); + + // Ensure that the removal update goes through. + TestThreadUtils.runOnUiThreadBlocking(() -> { + mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls[1], mSiteSuggestionUrls[2]); + }); + waitForTileRemoved(siteToDismiss); + Assert.assertEquals(2, tileContainer.getChildCount()); + final View snackbarButton = waitForSnackbar(mActivityTestRule.getActivity()); + + Assert.assertTrue(mMostVisitedSites.isUrlBlacklisted(mSiteSuggestionUrls[0])); + TestThreadUtils.runOnUiThreadBlocking(() -> { snackbarButton.callOnClick(); }); + + Assert.assertFalse(mMostVisitedSites.isUrlBlacklisted(mSiteSuggestionUrls[0])); + + // Ensure that the removal of the update goes through. + TestThreadUtils.runOnUiThreadBlocking( + () -> { mMostVisitedSites.setTileSuggestions(mSiteSuggestionUrls); }); + waitForTileAdded(siteToDismiss); + Assert.assertEquals(3, tileContainer.getChildCount()); + } + + private NewTabPageRecyclerView getRecyclerView() { + return mNtp.getNewTabPageView().getRecyclerView(); + } + + private TileGridLayout getTileGridLayout() { + ViewGroup newTabPageLayout = mNtp.getNewTabPageLayout(); + Assert.assertNotNull("Unable to retrieve the NewTabPageLayout.", newTabPageLayout); + + TileGridLayout tileGridLayout = newTabPageLayout.findViewById(R.id.tile_grid_layout); + Assert.assertNotNull("Unable to retrieve the TileGridLayout.", tileGridLayout); + return tileGridLayout; + } + + private View getTileViewFor(SiteSuggestion suggestion) { + View tileView = getTileGridLayout().getTileView(suggestion); + Assert.assertNotNull("Tile not found for suggestion " + suggestion.url, tileView); + + return tileView; + } + + private void invokeContextMenu(View view, int contextMenuItemId) throws ExecutionException { + TestTouchUtils.performLongClickOnMainSync( + InstrumentationRegistry.getInstrumentation(), view); + Assert.assertTrue(InstrumentationRegistry.getInstrumentation().invokeContextMenuAction( + mActivityTestRule.getActivity(), contextMenuItemId, 0)); + } + + /** Wait for the snackbar associated to a tile dismissal to be shown and returns its button. */ + private static View waitForSnackbar(final ChromeActivity activity) { + final String expectedSnackbarMessage = + activity.getResources().getString(R.string.most_visited_item_removed); + CriteriaHelper.pollUiThread(new Criteria("The snackbar was not shown.") { + @Override + public boolean isSatisfied() { + SnackbarManager snackbarManager = activity.getSnackbarManager(); + if (!snackbarManager.isShowing()) return false; + + TextView snackbarMessage = (TextView) activity.findViewById(R.id.snackbar_message); + if (snackbarMessage == null) return false; + + return snackbarMessage.getText().toString().equals(expectedSnackbarMessage); + } + }); + + return activity.findViewById(R.id.snackbar_button); + } + + private void waitForTileRemoved(final SiteSuggestion suggestion) + throws TimeoutException, InterruptedException { + TileGridLayout tileContainer = getTileGridLayout(); + final SuggestionsTileView removedTile = tileContainer.getTileView(suggestion); + if (removedTile == null) return; + + final CallbackHelper callback = new CallbackHelper(); + tileContainer.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() { + @Override + public void onChildViewAdded(View parent, View child) {} + + @Override + public void onChildViewRemoved(View parent, View child) { + if (child == removedTile) callback.notifyCalled(); + } + }); + callback.waitForCallback("The expected tile was not removed.", 0); + tileContainer.setOnHierarchyChangeListener(null); + } + + private void waitForTileAdded(final SiteSuggestion suggestion) + throws TimeoutException, InterruptedException { + TileGridLayout tileContainer = getTileGridLayout(); + if (tileContainer.getTileView(suggestion) != null) return; + + final CallbackHelper callback = new CallbackHelper(); + tileContainer.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() { + @Override + public void onChildViewAdded(View parent, View child) { + if (!(child instanceof SuggestionsTileView)) return; + if (!((SuggestionsTileView) child).getData().equals(suggestion)) return; + + callback.notifyCalled(); + } + + @Override + public void onChildViewRemoved(View parent, View child) {} + }); + callback.waitForCallback("The expected tile was not added.", 0); + tileContainer.setOnHierarchyChangeListener(null); + } + + private SiteSuggestion recreateSuggestionsWithExploreTile() { + // need a copy of the list in order to modify it. + final ArrayList<SiteSuggestion> currentSuggestions = + new ArrayList<>(mMostVisitedSites.getCurrentSites()); + + SiteSuggestion exploreTile = new SiteSuggestion("chrome-native://explore", + "chrome-native://explore", "", TileTitleSource.UNKNOWN, TileSource.EXPLORE, + TileSectionType.PERSONALIZED, new Date()); + currentSuggestions.add(exploreTile); + TestThreadUtils.runOnUiThreadBlocking( + () -> mMostVisitedSites.setTileSuggestions(currentSuggestions)); + + return exploreTile; + } +}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileGroupUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileGroupUnitTest.java deleted file mode 100644 index a6b737d..0000000 --- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileGroupUnitTest.java +++ /dev/null
@@ -1,505 +0,0 @@ -// 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.chrome.browser.suggestions; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import static org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites.createSiteSuggestion; - -import android.graphics.Bitmap; -import android.graphics.Color; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import org.hamcrest.CoreMatchers; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestRule; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; - -import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.chrome.browser.favicon.IconType; -import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback; -import org.chromium.chrome.browser.native_page.ContextMenuManager; -import org.chromium.chrome.browser.ntp.cards.CardsVariationParameters; -import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; -import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle; -import org.chromium.chrome.browser.widget.displaystyle.UiConfig; -import org.chromium.chrome.test.util.browser.Features; -import org.chromium.chrome.test.util.browser.suggestions.FakeMostVisitedSites; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * Unit tests for {@link TileGroup}. - */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class TileGroupUnitTest { - private static final int MAX_TILES_TO_FETCH = 4; - private static final int TILE_TITLE_LINES = 1; - private static final String[] URLS = {"https://www.google.com", "https://tellmedadjokes.com"}; - - @Rule - public TestRule mFeaturesProcessor = new Features.JUnitProcessor(); - - @Mock - private TileGroup.Observer mTileGroupObserver; - @Mock - private TileGroup.Delegate mTileGroupDelegate; - - private FakeMostVisitedSites mMostVisitedSites; - private FakeImageFetcher mImageFetcher; - private TileRenderer mTileRenderer; - - @Before - public void setUp() { - CardsVariationParameters.setTestVariationParams(new HashMap<>()); - MockitoAnnotations.initMocks(this); - - mImageFetcher = new FakeImageFetcher(); - mTileRenderer = new TileRenderer( - RuntimeEnvironment.application, TileStyle.MODERN, TILE_TITLE_LINES, mImageFetcher); - mMostVisitedSites = new FakeMostVisitedSites(); - - doAnswer(invocation -> { - mMostVisitedSites.setObserver( - invocation.getArgument(0), invocation.<Integer>getArgument(1)); - return null; - }) - .when(mTileGroupDelegate) - .setMostVisitedSitesObserver(any(MostVisitedSites.Observer.class), anyInt()); - } - - @After - public void tearDown() { - CardsVariationParameters.setTestVariationParams(null); - } - - @Test - public void testInitialiseWithTileList() { - mMostVisitedSites.setTileSuggestions(URLS); - - TileGroup tileGroup = new TileGroup(mTileRenderer, mock(SuggestionsUiDelegate.class), - mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver, - mock(OfflinePageBridge.class)); - tileGroup.startObserving(MAX_TILES_TO_FETCH); - - verify(mTileGroupObserver).onTileCountChanged(); - verify(mTileGroupObserver).onTileDataChanged(); - - verify(mTileGroupDelegate, never()).onLoadingComplete(any()); - assertTrue(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); - assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.FETCH_DATA)); - } - - /** - * Test the special case during initialisation, where we notify TileGroup.Observer of changes - * event though the data did not change (still empty just like before initialisation). - */ - @Test - public void testInitialiseWithEmptyTileList() { - TileGroup tileGroup = new TileGroup(mTileRenderer, mock(SuggestionsUiDelegate.class), - mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver, - mock(OfflinePageBridge.class)); - tileGroup.startObserving(MAX_TILES_TO_FETCH); - - verify(mTileGroupObserver).onTileCountChanged(); - verify(mTileGroupObserver).onTileDataChanged(); - - verify(mTileGroupDelegate, never()).onLoadingComplete(any()); - assertTrue(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); - assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.FETCH_DATA)); - } - - @Test - public void testReceiveNewTilesWithoutChanges() { - TileGroup tileGroup = initialiseTileGroup(URLS); - - // Notify the same thing. No changes so|mTileGroupObserver| should not be notified. - mMostVisitedSites.setTileSuggestions(URLS); - - verifyNoMoreInteractions(mTileGroupObserver); - verifyNoMoreInteractions(mTileGroupDelegate); - assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); - } - - @Test - public void testReceiveNewTilesWithoutChanges_TrackLoad() { - TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true, URLS); - - // Notify the same thing. No changes so|mTileGroupObserver| should not be notified. - mMostVisitedSites.setTileSuggestions(URLS); - tileGroup.onSwitchToForeground(/* trackLoadTask: */ true); - - verifyNoMoreInteractions(mTileGroupObserver); - verifyNoMoreInteractions(mTileGroupDelegate); - assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); - } - - @Test - public void testReceiveNewTilesWithDataChanges() { - TileGroup tileGroup = initialiseTileGroup(URLS); - - // Notify the about different URLs, but the same number. #onTileCountChanged() should not be - // called. - mMostVisitedSites.setTileSuggestions("foo", "bar"); - - verify(mTileGroupObserver, never()).onTileCountChanged(); // Tile count is still 2. - verify(mTileGroupObserver).onTileDataChanged(); // Data DID change. - - // No load task the second time. - verify(mTileGroupDelegate, never()).onLoadingComplete(any()); - assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); - } - - @Test - public void testReceiveNewTilesWithDataChanges_TrackLoad() { - TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true, URLS); - - // Notify the about different URLs, but the same number. #onTileCountChanged() should not be - // called. - mMostVisitedSites.setTileSuggestions("foo", "bar"); - tileGroup.onSwitchToForeground(/* trackLoadTask: */ true); - - verify(mTileGroupObserver).onTileDataChanged(); // Now data DID change. - verify(mTileGroupObserver, never()).onTileCountChanged(); // Tile count is still 2. - - // We should now have a pending task. - verify(mTileGroupDelegate, never()).onLoadingComplete(any()); - assertTrue(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); - } - - @Test - public void testReceiveNewTilesWithCountChanges() { - TileGroup tileGroup = initialiseTileGroup(URLS); - - mMostVisitedSites.setTileSuggestions(URLS[0]); - - verify(mTileGroupObserver).onTileCountChanged(); // Tile count DID change. - verify(mTileGroupObserver).onTileDataChanged(); // Data DID change. - verify(mTileGroupDelegate, never()) - .onLoadingComplete(any()); // No load task the second time. - assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); - } - - @Test - public void testTileLoadingWhenVisibleNotBlockedForInit() { - SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); - when(uiDelegate.isVisible()).thenReturn(true); - TileGroup tileGroup = - new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class), - mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class)); - tileGroup.startObserving(MAX_TILES_TO_FETCH); - - mMostVisitedSites.setTileSuggestions(URLS); - - // Because it's the first load, we accept the incoming tiles and refresh the view. - verify(mTileGroupObserver).onTileDataChanged(); - } - - @Test - public void testTileLoadingWhenVisibleBlocked() { - SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); - when(uiDelegate.isVisible()).thenReturn(true); - TileGroup tileGroup = - new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class), - mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class)); - tileGroup.startObserving(MAX_TILES_TO_FETCH); - - mMostVisitedSites.setTileSuggestions(URLS); - reset(mTileGroupObserver); - - mMostVisitedSites.setTileSuggestions(URLS[0]); - - // Even though the data changed, the notification should not happen because we want to not - // show changes to UI elements currently visible - verify(mTileGroupObserver, never()).onTileDataChanged(); - - // Simulating a switch from background to foreground should force the tilegrid to load the - // new data. - tileGroup.onSwitchToForeground(true); - verify(mTileGroupObserver).onTileDataChanged(); - } - - @Test - public void testTileLoadingWhenVisibleBlocked_2() { - TileGroup tileGroup = initialiseTileGroup(true, URLS); - - mMostVisitedSites.setTileSuggestions(URLS[0]); - - // Even though the data changed, the notification should not happen because we want to not - // show changes to UI elements currently visible - verify(mTileGroupObserver, never()).onTileDataChanged(); - - // Simulating a switch from background to foreground should force the tilegrid to load the - // new data. - tileGroup.onSwitchToForeground(true); - verify(mTileGroupObserver).onTileDataChanged(); - } - - @Test - public void testRenderTileView() { - SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); - when(uiDelegate.getImageFetcher()).thenReturn(mImageFetcher); - TileGroup tileGroup = - new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class), - mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class)); - tileGroup.startObserving(MAX_TILES_TO_FETCH); - - TileGridViewHolder tileGrid = setupView(tileGroup); - TileGridLayout layout = (TileGridLayout) tileGrid.itemView; - - // Initialise the internal list of tiles - mMostVisitedSites.setTileSuggestions(URLS); - - // Render them to the layout. - tileGrid.refreshData(); - assertThat(layout.getChildCount(), is(2)); - assertThat(((SuggestionsTileView) layout.getChildAt(0)).getUrl(), is(URLS[0])); - assertThat(((SuggestionsTileView) layout.getChildAt(1)).getUrl(), is(URLS[1])); - } - - /** Check for https://crbug.com/703628: don't crash on duplicated URLs. */ - @Test - public void testRenderTileViewWithDuplicatedUrl() { - SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); - when(uiDelegate.getImageFetcher()).thenReturn(mock(ImageFetcher.class)); - TileGroup tileGroup = - new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class), - mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class)); - tileGroup.startObserving(MAX_TILES_TO_FETCH); - TileGridViewHolder tileGrid = setupView(tileGroup); - - // Initialise the internal list of tiles - mMostVisitedSites.setTileSuggestions(URLS[0], URLS[1], URLS[0]); - - // Render them to the layout. The duplicated URL should not trigger an exception. - tileGrid.refreshData(); - } - - @Test - public void testRenderTileViewReplacing() { - SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); - when(uiDelegate.getImageFetcher()).thenReturn(mock(ImageFetcher.class)); - TileGroup tileGroup = - new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class), - mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class)); - tileGroup.startObserving(MAX_TILES_TO_FETCH); - mMostVisitedSites.setTileSuggestions(URLS); - - // Initialise the layout with views whose URLs don't match the ones of the new tiles. - TileGridViewHolder tileGrid = setupView(tileGroup); - TileGridLayout layout = (TileGridLayout) tileGrid.itemView; - SuggestionsTileView view1 = mock(SuggestionsTileView.class); - layout.addView(view1); - - SuggestionsTileView view2 = mock(SuggestionsTileView.class); - layout.addView(view2); - - // The tiles should be updated, the old ones removed. - tileGrid.refreshData(); - assertThat(layout.getChildCount(), is(2)); - assertThat(layout.indexOfChild(view1), is(-1)); - assertThat(layout.indexOfChild(view2), is(-1)); - } - - @Test - public void testRenderTileViewRecycling() { - mMostVisitedSites.setTileSuggestions(URLS); - List<SiteSuggestion> sites = mMostVisitedSites.getCurrentSites(); - TileGroup tileGroup = new TileGroup(mTileRenderer, mock(SuggestionsUiDelegate.class), - mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver, - mock(OfflinePageBridge.class)); - tileGroup.startObserving(MAX_TILES_TO_FETCH); - - // Initialise the layout with views whose URLs match the ones of the new tiles. - TileGridLayout layout = new TileGridLayout(RuntimeEnvironment.application, null); - SuggestionsTileView view1 = mock(SuggestionsTileView.class); - when(view1.getData()).thenReturn(sites.get(0)); - layout.addView(view1); - - SuggestionsTileView view2 = mock(SuggestionsTileView.class); - when(view2.getData()).thenReturn(sites.get(1)); - layout.addView(view2); - - // The tiles should be updated, the old ones reused. - setupView(tileGroup).refreshData(); - assertThat(layout.getChildCount(), is(2)); - assertThat(layout.getChildAt(0), CoreMatchers.is(view1)); - assertThat(layout.getChildAt(1), CoreMatchers.is(view2)); - } - - @Test - public void testIconLoadingForInit() { - TileGroup tileGroup = initialiseTileGroup(URLS); - Tile tile = tileGroup.getTileSections().get(TileSectionType.PERSONALIZED).get(0); - - // Loading complete should be delayed until the icons are done loading. - verify(mTileGroupDelegate, never()).onLoadingComplete(any()); - - mImageFetcher.fulfillLargeIconRequests(); - - // The load should now be complete. - verify(mTileGroupDelegate).onLoadingComplete(any()); - verify(mTileGroupObserver).onTileIconChanged(eq(tile)); - verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class)); - } - - @Test - public void testIconLoadingWhenTileNotRegistered() { - TileGroup tileGroup = initialiseTileGroup(); - Tile tile = new Tile(createSiteSuggestion("title", URLS[0]), 0); - - ViewGroup layout = new FrameLayout(RuntimeEnvironment.application, null); - mTileRenderer.buildTileView(tile, layout, tileGroup.getTileSetupDelegate()); - - // Ensure we run the callback for the new tile. - assertEquals(1, mImageFetcher.getPendingIconCallbackCount()); - mImageFetcher.fulfillLargeIconRequests(); - - verify(mTileGroupObserver, never()).onTileIconChanged(tile); - } - - private TileGridViewHolder setupView(TileGroup tileGroup) { - TileGridLayout layout = new TileGridLayout(RuntimeEnvironment.application, null); - TileGridViewHolder tileGrid = new TileGridViewHolder(layout, 4, 2, mock(UiConfig.class)); - tileGrid.bindDataSource(tileGroup, mTileRenderer); - return tileGrid; - } - - @Test - public void testIconLoading_Sync() { - TileGroup tileGroup = initialiseTileGroup(); - mImageFetcher.fulfillLargeIconRequests(); - reset(mTileGroupObserver, mTileGroupDelegate); - - // Notify for a second set. - mMostVisitedSites.setTileSuggestions(URLS); - setupView(tileGroup).refreshData(); - mImageFetcher.fulfillLargeIconRequests(); - - // Data changed but no loading complete event is sent - verify(mTileGroupObserver).onTileDataChanged(); - verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class)); - verify(mTileGroupDelegate, never()).onLoadingComplete(any()); - } - - @Test - public void testIconLoading_AsyncNoTrack() { - TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true); - mImageFetcher.fulfillLargeIconRequests(); - reset(mTileGroupObserver, mTileGroupDelegate); - - // Notify for a second set. - mMostVisitedSites.setTileSuggestions(URLS); - tileGroup.onSwitchToForeground(/* trackLoadTask: */ false); - setupView(tileGroup).refreshData(); - mImageFetcher.fulfillLargeIconRequests(); - - // Data changed but no loading complete event is sent (same as sync) - verify(mTileGroupObserver).onTileDataChanged(); - verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class)); - verify(mTileGroupDelegate, never()).onLoadingComplete(any()); - } - - @Test - public void testIconLoading_AsyncTrack() { - TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true); - mImageFetcher.fulfillLargeIconRequests(); - reset(mTileGroupObserver, mTileGroupDelegate); - - // Notify for a second set. - mMostVisitedSites.setTileSuggestions(URLS); - tileGroup.onSwitchToForeground(/* trackLoadTask: */ true); - setupView(tileGroup).refreshData(); - mImageFetcher.fulfillLargeIconRequests(); - - // Data changed but no loading complete event is sent - verify(mTileGroupObserver).onTileDataChanged(); - verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class)); - verify(mTileGroupDelegate).onLoadingComplete(any()); - } - - /** {@link #initialiseTileGroup(boolean, String...)} override that does not defer loads. */ - private TileGroup initialiseTileGroup(String... urls) { - return initialiseTileGroup(false, urls); - } - - /** - * @param deferLoad whether to defer the load until - * {@link TileGroup#onSwitchToForeground(boolean)} is called. Works by - * pretending that the UI is visible. - * @param urls URLs used to initialise the tile group. - */ - private TileGroup initialiseTileGroup(boolean deferLoad, String... urls) { - SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); - when(uiDelegate.getImageFetcher()).thenReturn(mImageFetcher); - when(uiDelegate.isVisible()).thenReturn(deferLoad); - - mMostVisitedSites.setTileSuggestions(urls); - - TileGroup tileGroup = - new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class), - mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class)); - tileGroup.startObserving(MAX_TILES_TO_FETCH); - setupView(tileGroup).refreshData(); - - reset(mTileGroupObserver); - reset(mTileGroupDelegate); - return tileGroup; - } - - private class FakeImageFetcher extends ImageFetcher { - private final List<LargeIconCallback> mCallbackList = new ArrayList<>(); - - public FakeImageFetcher() { - super(null, null, null); - } - - @Override - public void makeLargeIconRequest(String url, int size, LargeIconCallback callback) { - mCallbackList.add(callback); - } - - public void fulfillLargeIconRequests(Bitmap bitmap, int color, boolean isColorDefault) { - for (LargeIconCallback callback : mCallbackList) { - callback.onLargeIconAvailable(bitmap, color, isColorDefault, IconType.INVALID); - } - mCallbackList.clear(); - } - - public int getPendingIconCallbackCount() { - return mCallbackList.size(); - } - - public void fulfillLargeIconRequests() { - fulfillLargeIconRequests(mock(Bitmap.class), Color.BLACK, false); - } - } -}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java new file mode 100644 index 0000000..1421f69 --- /dev/null +++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java
@@ -0,0 +1,508 @@ +// 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.chrome.browser.suggestions.tile; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import static org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites.createSiteSuggestion; + +import android.graphics.Bitmap; +import android.graphics.Color; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import org.hamcrest.CoreMatchers; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.chrome.browser.favicon.IconType; +import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback; +import org.chromium.chrome.browser.native_page.ContextMenuManager; +import org.chromium.chrome.browser.ntp.cards.CardsVariationParameters; +import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; +import org.chromium.chrome.browser.suggestions.ImageFetcher; +import org.chromium.chrome.browser.suggestions.SiteSuggestion; +import org.chromium.chrome.browser.suggestions.SuggestionsConfig.TileStyle; +import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate; +import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites; +import org.chromium.chrome.test.util.browser.Features; +import org.chromium.chrome.test.util.browser.suggestions.mostvisited.FakeMostVisitedSites; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Unit tests for {@link TileGroup}. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class TileGroupUnitTest { + private static final int MAX_TILES_TO_FETCH = 4; + private static final int TILE_TITLE_LINES = 1; + private static final String[] URLS = {"https://www.google.com", "https://tellmedadjokes.com"}; + + @Rule + public TestRule mFeaturesProcessor = new Features.JUnitProcessor(); + + @Mock + private TileGroup.Observer mTileGroupObserver; + @Mock + private TileGroup.Delegate mTileGroupDelegate; + + private FakeMostVisitedSites mMostVisitedSites; + private FakeImageFetcher mImageFetcher; + private TileRenderer mTileRenderer; + + @Before + public void setUp() { + CardsVariationParameters.setTestVariationParams(new HashMap<>()); + MockitoAnnotations.initMocks(this); + + mImageFetcher = new FakeImageFetcher(); + mTileRenderer = new TileRenderer( + RuntimeEnvironment.application, TileStyle.MODERN, TILE_TITLE_LINES, mImageFetcher); + mMostVisitedSites = new FakeMostVisitedSites(); + + doAnswer(invocation -> { + mMostVisitedSites.setObserver( + invocation.getArgument(0), invocation.<Integer>getArgument(1)); + return null; + }) + .when(mTileGroupDelegate) + .setMostVisitedSitesObserver(any(MostVisitedSites.Observer.class), anyInt()); + } + + @After + public void tearDown() { + CardsVariationParameters.setTestVariationParams(null); + } + + @Test + public void testInitialiseWithTileList() { + mMostVisitedSites.setTileSuggestions(URLS); + + TileGroup tileGroup = new TileGroup(mTileRenderer, mock(SuggestionsUiDelegate.class), + mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver, + mock(OfflinePageBridge.class)); + tileGroup.startObserving(MAX_TILES_TO_FETCH); + + verify(mTileGroupObserver).onTileCountChanged(); + verify(mTileGroupObserver).onTileDataChanged(); + + verify(mTileGroupDelegate, never()).onLoadingComplete(any()); + assertTrue(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); + assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.FETCH_DATA)); + } + + /** + * Test the special case during initialisation, where we notify TileGroup.Observer of changes + * event though the data did not change (still empty just like before initialisation). + */ + @Test + public void testInitialiseWithEmptyTileList() { + TileGroup tileGroup = new TileGroup(mTileRenderer, mock(SuggestionsUiDelegate.class), + mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver, + mock(OfflinePageBridge.class)); + tileGroup.startObserving(MAX_TILES_TO_FETCH); + + verify(mTileGroupObserver).onTileCountChanged(); + verify(mTileGroupObserver).onTileDataChanged(); + + verify(mTileGroupDelegate, never()).onLoadingComplete(any()); + assertTrue(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); + assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.FETCH_DATA)); + } + + @Test + public void testReceiveNewTilesWithoutChanges() { + TileGroup tileGroup = initialiseTileGroup(URLS); + + // Notify the same thing. No changes so|mTileGroupObserver| should not be notified. + mMostVisitedSites.setTileSuggestions(URLS); + + verifyNoMoreInteractions(mTileGroupObserver); + verifyNoMoreInteractions(mTileGroupDelegate); + assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); + } + + @Test + public void testReceiveNewTilesWithoutChanges_TrackLoad() { + TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true, URLS); + + // Notify the same thing. No changes so|mTileGroupObserver| should not be notified. + mMostVisitedSites.setTileSuggestions(URLS); + tileGroup.onSwitchToForeground(/* trackLoadTask: */ true); + + verifyNoMoreInteractions(mTileGroupObserver); + verifyNoMoreInteractions(mTileGroupDelegate); + assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); + } + + @Test + public void testReceiveNewTilesWithDataChanges() { + TileGroup tileGroup = initialiseTileGroup(URLS); + + // Notify the about different URLs, but the same number. #onTileCountChanged() should not be + // called. + mMostVisitedSites.setTileSuggestions("foo", "bar"); + + verify(mTileGroupObserver, never()).onTileCountChanged(); // Tile count is still 2. + verify(mTileGroupObserver).onTileDataChanged(); // Data DID change. + + // No load task the second time. + verify(mTileGroupDelegate, never()).onLoadingComplete(any()); + assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); + } + + @Test + public void testReceiveNewTilesWithDataChanges_TrackLoad() { + TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true, URLS); + + // Notify the about different URLs, but the same number. #onTileCountChanged() should not be + // called. + mMostVisitedSites.setTileSuggestions("foo", "bar"); + tileGroup.onSwitchToForeground(/* trackLoadTask: */ true); + + verify(mTileGroupObserver).onTileDataChanged(); // Now data DID change. + verify(mTileGroupObserver, never()).onTileCountChanged(); // Tile count is still 2. + + // We should now have a pending task. + verify(mTileGroupDelegate, never()).onLoadingComplete(any()); + assertTrue(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); + } + + @Test + public void testReceiveNewTilesWithCountChanges() { + TileGroup tileGroup = initialiseTileGroup(URLS); + + mMostVisitedSites.setTileSuggestions(URLS[0]); + + verify(mTileGroupObserver).onTileCountChanged(); // Tile count DID change. + verify(mTileGroupObserver).onTileDataChanged(); // Data DID change. + verify(mTileGroupDelegate, never()) + .onLoadingComplete(any()); // No load task the second time. + assertFalse(tileGroup.isTaskPending(TileGroup.TileTask.SCHEDULE_ICON_FETCH)); + } + + @Test + public void testTileLoadingWhenVisibleNotBlockedForInit() { + SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); + when(uiDelegate.isVisible()).thenReturn(true); + TileGroup tileGroup = + new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class), + mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class)); + tileGroup.startObserving(MAX_TILES_TO_FETCH); + + mMostVisitedSites.setTileSuggestions(URLS); + + // Because it's the first load, we accept the incoming tiles and refresh the view. + verify(mTileGroupObserver).onTileDataChanged(); + } + + @Test + public void testTileLoadingWhenVisibleBlocked() { + SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); + when(uiDelegate.isVisible()).thenReturn(true); + TileGroup tileGroup = + new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class), + mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class)); + tileGroup.startObserving(MAX_TILES_TO_FETCH); + + mMostVisitedSites.setTileSuggestions(URLS); + reset(mTileGroupObserver); + + mMostVisitedSites.setTileSuggestions(URLS[0]); + + // Even though the data changed, the notification should not happen because we want to not + // show changes to UI elements currently visible + verify(mTileGroupObserver, never()).onTileDataChanged(); + + // Simulating a switch from background to foreground should force the tilegrid to load the + // new data. + tileGroup.onSwitchToForeground(true); + verify(mTileGroupObserver).onTileDataChanged(); + } + + @Test + public void testTileLoadingWhenVisibleBlocked_2() { + TileGroup tileGroup = initialiseTileGroup(true, URLS); + + mMostVisitedSites.setTileSuggestions(URLS[0]); + + // Even though the data changed, the notification should not happen because we want to not + // show changes to UI elements currently visible + verify(mTileGroupObserver, never()).onTileDataChanged(); + + // Simulating a switch from background to foreground should force the tilegrid to load the + // new data. + tileGroup.onSwitchToForeground(true); + verify(mTileGroupObserver).onTileDataChanged(); + } + + @Test + public void testRenderTileView() { + SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); + when(uiDelegate.getImageFetcher()).thenReturn(mImageFetcher); + TileGroup tileGroup = + new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class), + mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class)); + tileGroup.startObserving(MAX_TILES_TO_FETCH); + + TileGridViewHolder tileGrid = setupView(tileGroup); + TileGridLayout layout = (TileGridLayout) tileGrid.itemView; + + // Initialise the internal list of tiles + mMostVisitedSites.setTileSuggestions(URLS); + + // Render them to the layout. + tileGrid.refreshData(); + assertThat(layout.getChildCount(), is(2)); + assertThat(((SuggestionsTileView) layout.getChildAt(0)).getUrl(), is(URLS[0])); + assertThat(((SuggestionsTileView) layout.getChildAt(1)).getUrl(), is(URLS[1])); + } + + /** Check for https://crbug.com/703628: don't crash on duplicated URLs. */ + @Test + public void testRenderTileViewWithDuplicatedUrl() { + SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); + when(uiDelegate.getImageFetcher()).thenReturn(mock(ImageFetcher.class)); + TileGroup tileGroup = + new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class), + mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class)); + tileGroup.startObserving(MAX_TILES_TO_FETCH); + TileGridViewHolder tileGrid = setupView(tileGroup); + + // Initialise the internal list of tiles + mMostVisitedSites.setTileSuggestions(URLS[0], URLS[1], URLS[0]); + + // Render them to the layout. The duplicated URL should not trigger an exception. + tileGrid.refreshData(); + } + + @Test + public void testRenderTileViewReplacing() { + SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); + when(uiDelegate.getImageFetcher()).thenReturn(mock(ImageFetcher.class)); + TileGroup tileGroup = + new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class), + mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class)); + tileGroup.startObserving(MAX_TILES_TO_FETCH); + mMostVisitedSites.setTileSuggestions(URLS); + + // Initialise the layout with views whose URLs don't match the ones of the new tiles. + TileGridViewHolder tileGrid = setupView(tileGroup); + TileGridLayout layout = (TileGridLayout) tileGrid.itemView; + SuggestionsTileView view1 = mock(SuggestionsTileView.class); + layout.addView(view1); + + SuggestionsTileView view2 = mock(SuggestionsTileView.class); + layout.addView(view2); + + // The tiles should be updated, the old ones removed. + tileGrid.refreshData(); + assertThat(layout.getChildCount(), is(2)); + assertThat(layout.indexOfChild(view1), is(-1)); + assertThat(layout.indexOfChild(view2), is(-1)); + } + + @Test + public void testRenderTileViewRecycling() { + mMostVisitedSites.setTileSuggestions(URLS); + List<SiteSuggestion> sites = mMostVisitedSites.getCurrentSites(); + TileGroup tileGroup = new TileGroup(mTileRenderer, mock(SuggestionsUiDelegate.class), + mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver, + mock(OfflinePageBridge.class)); + tileGroup.startObserving(MAX_TILES_TO_FETCH); + + // Initialise the layout with views whose URLs match the ones of the new tiles. + TileGridLayout layout = new TileGridLayout(RuntimeEnvironment.application, null); + SuggestionsTileView view1 = mock(SuggestionsTileView.class); + when(view1.getData()).thenReturn(sites.get(0)); + layout.addView(view1); + + SuggestionsTileView view2 = mock(SuggestionsTileView.class); + when(view2.getData()).thenReturn(sites.get(1)); + layout.addView(view2); + + // The tiles should be updated, the old ones reused. + setupView(tileGroup).refreshData(); + assertThat(layout.getChildCount(), is(2)); + assertThat(layout.getChildAt(0), CoreMatchers.is(view1)); + assertThat(layout.getChildAt(1), CoreMatchers.is(view2)); + } + + @Test + public void testIconLoadingForInit() { + TileGroup tileGroup = initialiseTileGroup(URLS); + Tile tile = tileGroup.getTileSections().get(TileSectionType.PERSONALIZED).get(0); + + // Loading complete should be delayed until the icons are done loading. + verify(mTileGroupDelegate, never()).onLoadingComplete(any()); + + mImageFetcher.fulfillLargeIconRequests(); + + // The load should now be complete. + verify(mTileGroupDelegate).onLoadingComplete(any()); + verify(mTileGroupObserver).onTileIconChanged(eq(tile)); + verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class)); + } + + @Test + public void testIconLoadingWhenTileNotRegistered() { + TileGroup tileGroup = initialiseTileGroup(); + Tile tile = new Tile(createSiteSuggestion("title", URLS[0]), 0); + + ViewGroup layout = new FrameLayout(RuntimeEnvironment.application, null); + mTileRenderer.buildTileView(tile, layout, tileGroup.getTileSetupDelegate()); + + // Ensure we run the callback for the new tile. + assertEquals(1, mImageFetcher.getPendingIconCallbackCount()); + mImageFetcher.fulfillLargeIconRequests(); + + verify(mTileGroupObserver, never()).onTileIconChanged(tile); + } + + private TileGridViewHolder setupView(TileGroup tileGroup) { + TileGridLayout layout = new TileGridLayout(RuntimeEnvironment.application, null); + TileGridViewHolder tileGrid = new TileGridViewHolder(layout, 4, 2); + tileGrid.bindDataSource(tileGroup, mTileRenderer); + return tileGrid; + } + + @Test + public void testIconLoading_Sync() { + TileGroup tileGroup = initialiseTileGroup(); + mImageFetcher.fulfillLargeIconRequests(); + reset(mTileGroupObserver, mTileGroupDelegate); + + // Notify for a second set. + mMostVisitedSites.setTileSuggestions(URLS); + setupView(tileGroup).refreshData(); + mImageFetcher.fulfillLargeIconRequests(); + + // Data changed but no loading complete event is sent + verify(mTileGroupObserver).onTileDataChanged(); + verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class)); + verify(mTileGroupDelegate, never()).onLoadingComplete(any()); + } + + @Test + public void testIconLoading_AsyncNoTrack() { + TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true); + mImageFetcher.fulfillLargeIconRequests(); + reset(mTileGroupObserver, mTileGroupDelegate); + + // Notify for a second set. + mMostVisitedSites.setTileSuggestions(URLS); + tileGroup.onSwitchToForeground(/* trackLoadTask: */ false); + setupView(tileGroup).refreshData(); + mImageFetcher.fulfillLargeIconRequests(); + + // Data changed but no loading complete event is sent (same as sync) + verify(mTileGroupObserver).onTileDataChanged(); + verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class)); + verify(mTileGroupDelegate, never()).onLoadingComplete(any()); + } + + @Test + public void testIconLoading_AsyncTrack() { + TileGroup tileGroup = initialiseTileGroup(/* deferLoad: */ true); + mImageFetcher.fulfillLargeIconRequests(); + reset(mTileGroupObserver, mTileGroupDelegate); + + // Notify for a second set. + mMostVisitedSites.setTileSuggestions(URLS); + tileGroup.onSwitchToForeground(/* trackLoadTask: */ true); + setupView(tileGroup).refreshData(); + mImageFetcher.fulfillLargeIconRequests(); + + // Data changed but no loading complete event is sent + verify(mTileGroupObserver).onTileDataChanged(); + verify(mTileGroupObserver, times(URLS.length)).onTileIconChanged(any(Tile.class)); + verify(mTileGroupDelegate).onLoadingComplete(any()); + } + + /** {@link #initialiseTileGroup(boolean, String...)} override that does not defer loads. */ + private TileGroup initialiseTileGroup(String... urls) { + return initialiseTileGroup(false, urls); + } + + /** + * @param deferLoad whether to defer the load until + * {@link TileGroup#onSwitchToForeground(boolean)} is called. Works by + * pretending that the UI is visible. + * @param urls URLs used to initialise the tile group. + */ + private TileGroup initialiseTileGroup(boolean deferLoad, String... urls) { + SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); + when(uiDelegate.getImageFetcher()).thenReturn(mImageFetcher); + when(uiDelegate.isVisible()).thenReturn(deferLoad); + + mMostVisitedSites.setTileSuggestions(urls); + + TileGroup tileGroup = + new TileGroup(mTileRenderer, uiDelegate, mock(ContextMenuManager.class), + mTileGroupDelegate, mTileGroupObserver, mock(OfflinePageBridge.class)); + tileGroup.startObserving(MAX_TILES_TO_FETCH); + setupView(tileGroup).refreshData(); + + reset(mTileGroupObserver); + reset(mTileGroupDelegate); + return tileGroup; + } + + private class FakeImageFetcher extends ImageFetcher { + private final List<LargeIconCallback> mCallbackList = new ArrayList<>(); + + public FakeImageFetcher() { + super(null, null, null); + } + + @Override + public void makeLargeIconRequest(String url, int size, LargeIconCallback callback) { + mCallbackList.add(callback); + } + + public void fulfillLargeIconRequests(Bitmap bitmap, int color, boolean isColorDefault) { + for (LargeIconCallback callback : mCallbackList) { + callback.onLargeIconAvailable(bitmap, color, isColorDefault, IconType.INVALID); + } + mCallbackList.clear(); + } + + public int getPendingIconCallbackCount() { + return mCallbackList.size(); + } + + public void fulfillLargeIconRequests() { + fulfillLargeIconRequests(mock(Bitmap.class), Color.BLACK, false); + } + } +}
diff --git a/chrome/android/touchless/java/DEPS b/chrome/android/touchless/java/DEPS index 838459a9..81c3f45e 100644 --- a/chrome/android/touchless/java/DEPS +++ b/chrome/android/touchless/java/DEPS
@@ -1,3 +1,4 @@ include_rules = [ + "+components/feature_engagement/public", "+content/public/android/java/src/org/chromium/content_public", ]
diff --git a/chrome/android/touchless/java/res/layout/touchless_explore_sites_category_card_view.xml b/chrome/android/touchless/java/res/layout/touchless_explore_sites_category_card_view.xml index 45803dd..a3312e0 100644 --- a/chrome/android/touchless/java/res/layout/touchless_explore_sites_category_card_view.xml +++ b/chrome/android/touchless/java/res/layout/touchless_explore_sites_category_card_view.xml
@@ -24,7 +24,7 @@ android:textAppearance="@style/TextAppearance.BlackBodyDefault" tools:text="Category" /> - <org.chromium.chrome.browser.suggestions.TileGridLayout + <org.chromium.chrome.browser.suggestions.tile.TileGridLayout android:id="@+id/category_sites" android:layout_width="match_parent" android:layout_height="wrap_content"
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java index c079824..c1c96787 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/OpenLastTabMediator.java
@@ -41,6 +41,8 @@ class OpenLastTabMediator extends EmptyTabObserver implements HistoryProvider.BrowsingHistoryObserver, FocusableComponent { private static final String FIRST_LAUNCHED_KEY = "TOUCHLESS_WAS_FIRST_LAUNCHED"; + private static final String LAST_REMOVED_VISIT_TIMESTAMP_KEY = + "TOUCHLESS_LAST_REMOVED_VISIT_TIMESTAMP"; private final Context mContext; private final Profile mProfile; @@ -52,6 +54,10 @@ private final RoundedIconGenerator mIconGenerator; private final LargeIconBridge mIconBridge; + private HistoryItem mHistoryItem; + private boolean mPreferencesRead; + private long mLastRemovedVisitTimestamp = Long.MIN_VALUE; + OpenLastTabMediator(Context context, Profile profile, NativePageHost nativePageHost, PropertyModel model, OpenLastTabView view) { mModel = model; @@ -67,22 +73,33 @@ ViewUtils.createDefaultRoundedIconGenerator(mContext.getResources(), false); mIconBridge = new LargeIconBridge(mProfile); - PostTask.postTask(TaskTraits.USER_VISIBLE, () -> { - // Check if this is a first launch of Chrome. - SharedPreferences prefs = mNativePageHost.getActiveTab().getActivity().getPreferences( - Context.MODE_PRIVATE); - boolean firstLaunched = prefs.getBoolean(FIRST_LAUNCHED_KEY, true); - prefs.edit().putBoolean(FIRST_LAUNCHED_KEY, false).apply(); - PostTask.postTask(UiThreadTaskTraits.USER_VISIBLE, () -> { - mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_FIRST_LAUNCH, firstLaunched); - }); - }); + readPreferences(); // TODO(wylieb):Investigate adding an item limit to the API. // Query the history for everything (no API exists to only query for the most recent). refreshHistoryData(); } + private SharedPreferences getSharedPreferences() { + return mNativePageHost.getActiveTab().getActivity().getPreferences(Context.MODE_PRIVATE); + } + + private void readPreferences() { + PostTask.postTask(TaskTraits.USER_VISIBLE, () -> { + // Check if this is a first launch of Chrome. + SharedPreferences prefs = getSharedPreferences(); + boolean firstLaunched = prefs.getBoolean(FIRST_LAUNCHED_KEY, true); + prefs.edit().putBoolean(FIRST_LAUNCHED_KEY, false).apply(); + mLastRemovedVisitTimestamp = + prefs.getLong(LAST_REMOVED_VISIT_TIMESTAMP_KEY, Long.MIN_VALUE); + PostTask.postTask(UiThreadTaskTraits.USER_VISIBLE, () -> { + mPreferencesRead = true; + mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_FIRST_LAUNCH, firstLaunched); + updateModel(); + }); + }); + } + void destroy() { if (mHistoryBridge != null) { mHistoryBridge.destroy(); @@ -115,41 +132,55 @@ @Override public void onQueryHistoryComplete(List<HistoryItem> items, boolean hasMorePotentialMatches) { if (items.size() == 0) { + mHistoryItem = null; + } else { + // First item is most recent. + mHistoryItem = items.get(0); + } + if (mPreferencesRead) { + updateModel(); + } + } + + // updateModel is only called after preferences are read. It populates model with data from + // mHistoryItem. + private void updateModel() { + if (mHistoryItem == null || mHistoryItem.getTimestamp() <= mLastRemovedVisitTimestamp) { // Consider the case where the history has nothing in it to be a failure. mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_LOAD_SUCCESS, false); return; } - // First item is most recent. - HistoryItem item = items.get(0); - - String title = UrlUtilities.getDomainAndRegistry(item.getUrl(), false); + String title = UrlUtilities.getDomainAndRegistry(mHistoryItem.getUrl(), false); // Default the timestamp to happening just now. If it happened over a minute ago, calculate // and set the relative timestamp string. String timestamp = mContext.getResources().getString(R.string.open_last_tab_just_now); long now = System.currentTimeMillis(); - if (now - item.getTimestamp() > MINUTE_IN_MILLIS) { - timestamp = getRelativeTimeSpanString(item.getTimestamp(), now, MINUTE_IN_MILLIS) - .toString(); + if (now - mHistoryItem.getTimestamp() > MINUTE_IN_MILLIS) { + timestamp = + getRelativeTimeSpanString(mHistoryItem.getTimestamp(), now, MINUTE_IN_MILLIS) + .toString(); } mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_TITLE, title); mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_TIMESTAMP, timestamp); - boolean willReturnIcon = mIconBridge.getLargeIconForUrl(item.getUrl(), + boolean willReturnIcon = mIconBridge.getLargeIconForUrl(mHistoryItem.getUrl(), mContext.getResources().getDimensionPixelSize(R.dimen.open_last_tab_icon_size), (icon, fallbackColor, isFallbackColorDefault, iconType) -> { - setAndShowButton(item.getUrl(), icon, fallbackColor, title); + setAndShowButton(mHistoryItem.getUrl(), icon, fallbackColor, title); }); // False if icon bridge won't call us back. if (!willReturnIcon) { - setAndShowButton(item.getUrl(), null, R.color.default_icon_color, title); + setAndShowButton(mHistoryItem.getUrl(), null, R.color.default_icon_color, title); } mModel.set(OpenLastTabProperties.OPEN_LAST_TAB_LOAD_SUCCESS, true); } @Override - public void onHistoryDeleted() {} + public void onHistoryDeleted() { + refreshHistoryData(); + } @Override public void hasOtherFormsOfBrowsingData(boolean hasOtherForms) {} @@ -172,7 +203,22 @@ @Override public boolean isItemSupported( @ContextMenuManager.ContextMenuItemId int menuItemId) { - return menuItemId == ContextMenuManager.ContextMenuItemId.ADD_TO_MY_APPS; + return menuItemId == ContextMenuManager.ContextMenuItemId.ADD_TO_MY_APPS + || menuItemId == ContextMenuManager.ContextMenuItemId.REMOVE; + } + + @Override + public void removeItem() { + mLastRemovedVisitTimestamp = mHistoryItem.getTimestamp(); + PostTask.postTask(TaskTraits.USER_VISIBLE, () -> { + SharedPreferences prefs = getSharedPreferences(); + prefs.edit() + .putLong(LAST_REMOVED_VISIT_TIMESTAMP_KEY, + mLastRemovedVisitTimestamp) + .apply(); + }); + mHistoryBridge.markItemForRemoval(mHistoryItem); + mHistoryBridge.removeItems(); } @Override
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsMediator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsMediator.java index 0c22dd26..e19bb5a 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsMediator.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsMediator.java
@@ -16,9 +16,9 @@ import org.chromium.chrome.browser.explore_sites.ExploreSitesEnums; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.suggestions.ImageFetcher; -import org.chromium.chrome.browser.suggestions.MostVisitedSites; import org.chromium.chrome.browser.suggestions.SiteSuggestion; import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory; +import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites; import org.chromium.chrome.touchless.R; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel;
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java index 06a1f093..4106eb0 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java
@@ -7,6 +7,9 @@ import android.view.LayoutInflater; import org.chromium.chrome.browser.ActivityTabProvider; +import org.chromium.chrome.browser.feature_engagement.TrackerFactory; +import org.chromium.chrome.browser.preferences.ChromePreferenceManager; +import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.touchless.ui.tooltip.TooltipView; import org.chromium.chrome.touchless.R; import org.chromium.ui.modelutil.PropertyModel; @@ -27,7 +30,9 @@ (KeyFunctionsIPHView) LayoutInflater.from(tooltipView.getContext()) .inflate(R.layout.notouch_key_functions_view, null); PropertyModelChangeProcessor.create(model, view, KeyFunctionsIPHViewBinder::bind); - mMediator = new KeyFunctionsIPHMediator(model, activityTabProvider); + mMediator = new KeyFunctionsIPHMediator(model, activityTabProvider, + ChromePreferenceManager.getInstance(), + TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile())); } public void destroy() {
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java index 90461bd..1194c58 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java
@@ -9,11 +9,10 @@ import org.chromium.base.task.PostTask; import org.chromium.chrome.browser.ActivityTabProvider; import org.chromium.chrome.browser.dom_distiller.TabDistillabilityProvider; -import org.chromium.chrome.browser.feature_engagement.TrackerFactory; import org.chromium.chrome.browser.native_page.NativePageFactory; import org.chromium.chrome.browser.preferences.ChromePreferenceManager; -import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.components.feature_engagement.Tracker; import org.chromium.components.feature_engagement.Tracker.DisplayLockHandle; import org.chromium.content_public.browser.UiThreadTaskTraits; import org.chromium.ui.modelutil.PropertyModel; @@ -30,9 +29,11 @@ public class KeyFunctionsIPHMediator implements CursorObserver { private final PropertyModel mModel; private final KeyFunctionsIPHTabObserver mKeyFunctionsIPHTabObserver; + private final ChromePreferenceManager mChromePreferenceManager; + private final Tracker mTracker; private FutureTask mHideTask; - private int mPageLoadCount; private DisplayLockHandle mDisplayLockHandle; + private int mPageLoadCount; private boolean mIsFallbackCursorModeOn; private boolean mShowedWhenPageLoadStarted; @@ -40,8 +41,8 @@ // For the first INTRODUCTORY_SESSIONS sessions, show the IPH every INTRODUCTORY_PAGE_LOAD_CYCLE // page loads. - private static final int INTRODUCTORY_SESSIONS = 6; - private static final int INTRODUCTORY_PAGE_LOAD_CYCLE = 3; + static final int INTRODUCTORY_SESSIONS = 6; + static final int INTRODUCTORY_PAGE_LOAD_CYCLE = 3; @IntDef({DisplayCause.PAGE_LOAD_STARTED, DisplayCause.PAGE_LOAD_FINISHED, DisplayCause.FALLBACK_CURSOR_TOGGLED}) @@ -52,9 +53,12 @@ int FALLBACK_CURSOR_TOGGLED = 2; } - KeyFunctionsIPHMediator(PropertyModel model, ActivityTabProvider activityTabProvider) { + KeyFunctionsIPHMediator(PropertyModel model, ActivityTabProvider activityTabProvider, + ChromePreferenceManager chromePreferenceManager, Tracker tracker) { mModel = model; mKeyFunctionsIPHTabObserver = new KeyFunctionsIPHTabObserver(activityTabProvider); + mChromePreferenceManager = chromePreferenceManager; + mTracker = tracker; TouchlessEventHandler.addCursorObserver(this); } @@ -70,14 +74,13 @@ private void show(@DisplayCause int displayCause) { if (displayCause == DisplayCause.PAGE_LOAD_STARTED) { mShowedWhenPageLoadStarted = false; - int totalSessionCount = ChromePreferenceManager.getInstance().readInt( + int totalSessionCount = mChromePreferenceManager.readInt( ChromePreferenceManager.TOUCHLESS_BROWSING_SESSION_COUNT); if (totalSessionCount <= INTRODUCTORY_SESSIONS && mPageLoadCount % INTRODUCTORY_PAGE_LOAD_CYCLE != 1) { return; } if (totalSessionCount > INTRODUCTORY_SESSIONS && mPageLoadCount > 1) return; - mShowedWhenPageLoadStarted = true; } else if (mShowedWhenPageLoadStarted && displayCause == DisplayCause.PAGE_LOAD_FINISHED) { // If we have already shown the IPH when page load started, we should avoid showing it // again when page load is finished. @@ -86,11 +89,12 @@ // If we are already showing this IPH, we should release the lock. if (mDisplayLockHandle != null) mDisplayLockHandle.release(); - mDisplayLockHandle = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile()) - .acquireDisplayLock(); + mDisplayLockHandle = mTracker.acquireDisplayLock(); // If another IPH UI is currently shown, return. if (mDisplayLockHandle == null) return; + if (displayCause == DisplayCause.PAGE_LOAD_STARTED) mShowedWhenPageLoadStarted = true; + if (mHideTask != null) mHideTask.cancel(false); mHideTask = new FutureTask<Void>(() -> { mModel.set(KeyFunctionsIPHProperties.IS_VISIBLE, false);
diff --git a/chrome/android/touchless/junit/DEPS b/chrome/android/touchless/junit/DEPS new file mode 100644 index 0000000..2e2fc77 --- /dev/null +++ b/chrome/android/touchless/junit/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+components/feature_engagement/public", +]
diff --git a/chrome/android/touchless/junit/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediatorTest.java b/chrome/android/touchless/junit/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediatorTest.java new file mode 100644 index 0000000..ed68378 --- /dev/null +++ b/chrome/android/touchless/junit/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediatorTest.java
@@ -0,0 +1,126 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.touchless.ui.iph; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowLooper; + +import org.chromium.base.task.test.ShadowPostTask; +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.chrome.browser.ActivityTabProvider; +import org.chromium.chrome.browser.feature_engagement.TrackerFactory; +import org.chromium.chrome.browser.preferences.ChromePreferenceManager; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TabObserver; +import org.chromium.components.feature_engagement.Tracker; +import org.chromium.ui.modelutil.PropertyModel; + +/** + * Unit tests for the {@link KeyFunctionsIPHMediator} class. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE, shadows = {ShadowPostTask.class}) +public class KeyFunctionsIPHMediatorTest { + private static final String SAMPLE_URL = "https://google.com/chrome/"; + + private PropertyModel mModel; + private ArgumentCaptor<TabObserver> mTabObserver; + private Tab mTab; + private ChromePreferenceManager mChromePreferenceManager; + private Tracker mTracker; + private KeyFunctionsIPHMediator mKeyFunctionsIPHMediator; + + @Before + public void setUp() { + mTab = mock(Tab.class); + mChromePreferenceManager = mock(ChromePreferenceManager.class); + mTracker = mock(Tracker.class); + mTabObserver = ArgumentCaptor.forClass(TabObserver.class); + mModel = spy(new PropertyModel.Builder(KeyFunctionsIPHProperties.ALL_KEYS).build()); + ActivityTabProvider activityTabProvider = spy(ActivityTabProvider.class); + + TrackerFactory.setTrackerForTests(mTracker); + when(mTracker.acquireDisplayLock()).thenReturn(mock(Tracker.DisplayLockHandle.class)); + when(activityTabProvider.get()).thenReturn(mTab); + when(mChromePreferenceManager.readInt( + ChromePreferenceManager.TOUCHLESS_BROWSING_SESSION_COUNT)) + .thenReturn(0); + mKeyFunctionsIPHMediator = spy(new KeyFunctionsIPHMediator( + mModel, activityTabProvider, mChromePreferenceManager, mTracker)); + verify(mTab).addObserver(mTabObserver.capture()); + } + + @Test + public void visibilityTest() { + when(mTab.getUrl()).thenReturn(SAMPLE_URL); + mTabObserver.getValue().onPageLoadStarted(mTab, SAMPLE_URL); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_CURSOR_VISIBLE), false); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), true); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), false); + + for (int i = 1; i < KeyFunctionsIPHMediator.INTRODUCTORY_PAGE_LOAD_CYCLE; i++) { + mTabObserver.getValue().onPageLoadStarted(mTab, SAMPLE_URL); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), false); + } + + mTabObserver.getValue().onPageLoadStarted(mTab, SAMPLE_URL); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), true); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + + when(mChromePreferenceManager.readInt( + ChromePreferenceManager.TOUCHLESS_BROWSING_SESSION_COUNT)) + .thenReturn(KeyFunctionsIPHMediator.INTRODUCTORY_SESSIONS + 1); + for (int i = 0; i <= KeyFunctionsIPHMediator.INTRODUCTORY_PAGE_LOAD_CYCLE; i++) { + mTabObserver.getValue().onPageLoadStarted(mTab, SAMPLE_URL); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), false); + } + } + + @Test + public void cursorVisibilityToggleTest() { + mKeyFunctionsIPHMediator.onFallbackCursorModeToggled(true); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_CURSOR_VISIBLE), true); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), true); + + mKeyFunctionsIPHMediator.onFallbackCursorModeToggled(false); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_CURSOR_VISIBLE), false); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), true); + + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), false); + + mKeyFunctionsIPHMediator.onFallbackCursorModeToggled(true); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_CURSOR_VISIBLE), true); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), true); + } + + @Test + public void otherIPHShowingTest() { + when(mTracker.acquireDisplayLock()).thenReturn(null); + mKeyFunctionsIPHMediator.onFallbackCursorModeToggled(true); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), false); + + when(mTracker.acquireDisplayLock()).thenReturn(mock(Tracker.DisplayLockHandle.class)); + mKeyFunctionsIPHMediator.onFallbackCursorModeToggled(false); + Assert.assertEquals(mModel.get(KeyFunctionsIPHProperties.IS_VISIBLE), true); + } + + @After + public void tearDown() { + TrackerFactory.setTrackerForTests(null); + } +}
diff --git a/chrome/android/touchless/touchless_java_sources.gni b/chrome/android/touchless/touchless_java_sources.gni index fbba84c6..d062140 100644 --- a/chrome/android/touchless/touchless_java_sources.gni +++ b/chrome/android/touchless/touchless_java_sources.gni
@@ -75,6 +75,7 @@ touchless_junit_test_java_sources = [ "touchless/junit/src/org/chromium/chrome/browser/touchless/ScrollPositionInfoTest.java", "touchless/junit/src/org/chromium/chrome/browser/touchless/TouchlessActionItemViewHolderTest.java", + "touchless/junit/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediatorTest.java", "touchless/junit/src/org/chromium/chrome/browser/touchless/ui/progressbar/ProgressBarMediatorTest.java", ]
diff --git a/chrome/android/webapk/PRESUBMIT.py b/chrome/android/webapk/PRESUBMIT.py index 70d259d..9a1474d4 100644 --- a/chrome/android/webapk/PRESUBMIT.py +++ b/chrome/android/webapk/PRESUBMIT.py
@@ -7,12 +7,18 @@ See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for more details about the presubmit API built into depot_tools. -This presubmit checks for two rules: +This presubmit checks for three rules: 1. If anything in the webapk/libs/common or the webapk/shell_apk directories has changed (excluding test files), $CURRENT_VERSION_VARIABLE should be updated. 2. If $REQUEST_UPDATE_FOR_VERSION_VARIABLE in $REQUEST_UPDATE_FOR_VERSION_LOCAL_PATH is changed, the variable change should be the only change in the CL. +3. If a file in a res/ directory has been added, its' file name should be +unique to the res/ directory. + res/values/dimens.xml and res/values-v17/dimens.xml -> OK + res/values/dimens.xml and libs/common/res_splash/values/dimens.xml -> BAD +This requirement is needed to upload the resources to the Google storage +build bucket. """ CURRENT_VERSION_VARIABLE = 'current_shell_apk_version' @@ -24,11 +30,14 @@ TRIGGER_CURRENT_VERSION_UPDATE_LOCAL_PATHS = [ 'libs/common/src/', + 'libs/common/res_splash/', 'shell_apk/AndroidManifest.xml', 'shell_apk/res/', 'shell_apk/src/', ] +RES_DIR_LOCAL_PATHS = ['shell_apk/res', 'libs/common/res_splash'] + def _DoChangedContentsContain(changed_contents, key): for _, line in changed_contents: if key in line: @@ -36,6 +45,19 @@ return False +def _FindFileNamesInDirectory(input_api, dir_path, search_file_names): + """ + Searches the directory recursively for files with the passed-in file name + (not file path) set. Returns the file names of any matches. + """ + matches = [] + for _, _, file_names in input_api.os_walk(dir_path): + for file_name in file_names: + if file_name in search_file_names: + matches.append(file_name) + return matches + + def _CheckVersionVariableChanged(input_api, version_file_local_path, variable_name): for f in input_api.AffectedFiles(): @@ -92,11 +114,54 @@ return [] +def _CheckNoOverlappingFileNamesInResourceDirsRule(input_api, output_api): + """ + Checks that if a file has been added to a res/ directory that its file name + is unique to the res/ directory. + res/values/dimens.xml and res/values-v17/dimens.xml -> OK + res/values/dimens.xml and libs/common/res_splash/values/dimens.xml -> BAD + """ + res_dir_file_names_map = {} + for f in input_api.AffectedFiles(): + local_path = input_api.os_path.relpath(f.AbsoluteLocalPath(), + input_api.PresubmitLocalPath()) + for res_dir_local_path in RES_DIR_LOCAL_PATHS: + if local_path.startswith(res_dir_local_path): + file_name = input_api.os_path.basename(local_path) + res_dir_file_names_map.setdefault(res_dir_local_path, set()).add( + file_name) + break + + if len(res_dir_file_names_map) == 0: + return [] + + overlapping_file_names = set() + for res_dir, file_names in res_dir_file_names_map.items(): + for other_res_dir, other_file_names in res_dir_file_names_map.items(): + if res_dir == other_res_dir: + continue + + # Check for affected files with identical name in |other_res_dir|. + overlapping_file_names |= (file_names & other_file_names) + + # Check for existing files with identical name in |other_res_dir|. + overlapping_file_names.update( + _FindFileNamesInDirectory(input_api, other_res_dir, file_names)) + + if len(overlapping_file_names) > 0: + error_msg = ('Resources in different top level res/ directories {} should ' + 'have different names:').format(RES_DIR_LOCAL_PATHS) + return [output_api.PresubmitError(error_msg, + items=list(overlapping_file_names))] + return [] + def _CommonChecks(input_api, output_api): """Checks common to both upload and commit.""" result = [] result.extend(_CheckChromeUpdateTriggerRule(input_api, output_api)) result.extend(_CheckCurrentVersionIncreaseRule(input_api, output_api)) + result.extend(_CheckNoOverlappingFileNamesInResourceDirsRule(input_api, + output_api)) return result
diff --git a/chrome/android/webapk/PRESUBMIT_test.py b/chrome/android/webapk/PRESUBMIT_test.py index db2368d..674f780 100755 --- a/chrome/android/webapk/PRESUBMIT_test.py +++ b/chrome/android/webapk/PRESUBMIT_test.py
@@ -14,12 +14,36 @@ from PRESUBMIT_test_mocks import MockAffectedFile from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi -class ShellApkVersion(unittest.TestCase): - UPDATE_CURRENT_VERSION_MESSAGE = ( - 'current_shell_apk_version in ' - 'shell_apk/current_version/current_version.gni needs to updated due to ' - 'changes in:') +# Mocks os.walk() +class MockOsWalkFileSystem(object): + def __init__(self, file_paths): + self.file_paths = file_paths + def walk(self, top): + if not top.endswith('/'): + top += '/' + + files = [] + dirs = [] + for f in self.file_paths: + if f.startswith(top): + remaining = f[len(top):] + slash_index = remaining.find('/') + if slash_index >= 0: + dir_name = remaining[:slash_index] + if not dir_name in dirs: + dirs.append(dir_name) + else: + files.append(remaining) + + yield top[:-1], dirs, files + + for name in dirs: + for result in self.walk(top + name): + yield result + + +class CustomMockInputApi(MockInputApi): def makeMockAffectedFiles(self, file_names): mock_files = [] for file_name in file_names: @@ -27,6 +51,13 @@ MockAffectedFile(file_name, ['new_content'], action='A')) return mock_files + +class ShellApkVersion(unittest.TestCase): + UPDATE_CURRENT_VERSION_MESSAGE = ( + 'current_shell_apk_version in ' + 'shell_apk/current_version/current_version.gni needs to updated due to ' + 'changes in:') + def testCheckWamMintTriggerRule(self): COMMON_SRC_FILE_PATH = ( 'libs/common/src/org/chromium/webapk/lib/common/A.java') @@ -46,8 +77,8 @@ # template_shell_apk_version not updated. There should be a warning about # template_shell_apk_version needing to be updated. - input_api = MockInputApi() - input_api.files = self.makeMockAffectedFiles( + input_api = CustomMockInputApi() + input_api.files = input_api.makeMockAffectedFiles( changed_java_file_paths + [SHELL_APK_RES_FILE_PATH]) input_api.files += [ MockAffectedFile(CURRENT_VERSION_FILE_PATH, 'variable=O', @@ -62,7 +93,7 @@ warnings[0].items) # template_shell_apk_version updated. There should be no warnings. - input_api.files = self.makeMockAffectedFiles( + input_api.files = input_api.makeMockAffectedFiles( changed_java_file_paths + [SHELL_APK_RES_FILE_PATH]) input_api.files += [ MockAffectedFile(CURRENT_VERSION_FILE_PATH, @@ -73,5 +104,59 @@ MockOutputApi()) self.assertEqual([], warnings) + +class OverlappingResourceFileNames(unittest.TestCase): + RESOURCES_SHOULD_HAVE_DIFFERENT_FILE_NAMES_MESSAGE = ( + 'Resources in different top level res/ directories [\'shell_apk/res\', ' + '\'libs/common/res_splash\'] should have different names:') + + def testAddFileSameNameWithinResDirectory(self): + # Files within a res/ directory can have same file name. + MOCK_FILE_SYSTEM_FILES = ['shell_apk/res/values/colors.xml', + 'libs/common/res_splash/values/dimens.xml'] + input_api = CustomMockInputApi() + input_api.os_walk = MockOsWalkFileSystem(MOCK_FILE_SYSTEM_FILES).walk + + input_api.files = input_api.makeMockAffectedFiles([ + 'shell_apk/res/values-v22/values.xml']) + errors = PRESUBMIT._CheckNoOverlappingFileNamesInResourceDirsRule( + input_api, MockOutputApi()) + self.assertEqual(0, len(errors)) + + def testAddFileSameNameAcrossResDirectories(self): + # Adding a file to a res/ directory with the same file name as a file in a + # different res/ directory is illegal. + MOCK_FILE_SYSTEM_FILES = ['shell_apk/res/values/colors.xml', + 'libs/common/res_splash/values/dimens.xml'] + input_api = CustomMockInputApi() + input_api.os_walk = MockOsWalkFileSystem(MOCK_FILE_SYSTEM_FILES).walk + input_api.files = input_api.makeMockAffectedFiles([ + 'shell_apk/res/values-v17/dimens.xml', + 'libs/common/res_splash/values-v22/colors.xml']) + errors = PRESUBMIT._CheckNoOverlappingFileNamesInResourceDirsRule( + input_api, MockOutputApi()) + self.assertEqual(1, len(errors)) + self.assertEqual(self.RESOURCES_SHOULD_HAVE_DIFFERENT_FILE_NAMES_MESSAGE, + errors[0].message) + errors[0].items.sort() + self.assertEqual(['colors.xml', 'dimens.xml'], errors[0].items) + + def testAddTwoFilesWithSameNameDifferentResDirectories(self): + # Adding two files with the same file name but in different res/ + # directories is illegal. + MOCK_FILE_SYSTEM_FILES = ['shell_apk/res/values/colors.xml', + 'libs/common/res_splash/values/dimens.xml'] + input_api = CustomMockInputApi() + input_api.os_walk = MockOsWalkFileSystem(MOCK_FILE_SYSTEM_FILES).walk + input_api.files = input_api.makeMockAffectedFiles([ + 'shell_apk/res/values/values.xml', + 'libs/common/res_splash/values-v22/values.xml']) + errors = PRESUBMIT._CheckNoOverlappingFileNamesInResourceDirsRule( + input_api, MockOutputApi()) + self.assertEqual(1, len(errors)) + self.assertEqual(self.RESOURCES_SHOULD_HAVE_DIFFERENT_FILE_NAMES_MESSAGE, + errors[0].message) + self.assertEqual(['values.xml'], errors[0].items) + if __name__ == '__main__': unittest.main()
diff --git a/chrome/android/webapk/libs/common/res_splash/values-sw600dp/dimens.xml b/chrome/android/webapk/libs/common/res_splash/values-sw600dp/common_dimens.xml similarity index 100% rename from chrome/android/webapk/libs/common/res_splash/values-sw600dp/dimens.xml rename to chrome/android/webapk/libs/common/res_splash/values-sw600dp/common_dimens.xml
diff --git a/chrome/android/webapk/libs/common/res_splash/values-v17/styles.xml b/chrome/android/webapk/libs/common/res_splash/values-v17/common_styles.xml similarity index 100% rename from chrome/android/webapk/libs/common/res_splash/values-v17/styles.xml rename to chrome/android/webapk/libs/common/res_splash/values-v17/common_styles.xml
diff --git a/chrome/android/webapk/libs/common/res_splash/values/colors.xml b/chrome/android/webapk/libs/common/res_splash/values/common_colors.xml similarity index 100% rename from chrome/android/webapk/libs/common/res_splash/values/colors.xml rename to chrome/android/webapk/libs/common/res_splash/values/common_colors.xml
diff --git a/chrome/android/webapk/libs/common/res_splash/values/dimens.xml b/chrome/android/webapk/libs/common/res_splash/values/common_dimens.xml similarity index 100% rename from chrome/android/webapk/libs/common/res_splash/values/dimens.xml rename to chrome/android/webapk/libs/common/res_splash/values/common_dimens.xml
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni index 3123c861..51b231c 100644 --- a/chrome/android/webapk/shell_apk/current_version/current_version.gni +++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@ # //chrome/android/webapk/shell_apk:webapk is changed. This includes # Java files, Android resource files and AndroidManifest.xml. Does not affect # Chrome.apk -current_shell_apk_version = 91 +current_shell_apk_version = 92
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS index 5b8547f..93c5623 100644 --- a/chrome/browser/DEPS +++ b/chrome/browser/DEPS
@@ -135,6 +135,14 @@ "+chrome/browser/ui/views/extensions/request_file_system_dialog_view.h", "+chrome/browser/ui/views/try_chrome_dialog_win/try_chrome_dialog.h", + # Ensure that only the public interface of performance_manager gets used. + "-chrome/browser/performance_manager", + "+chrome/browser/performance_manager/public", + "+chrome/browser/performance_manager/chrome_browser_main_extra_parts_performance_manager.h", + "+chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.h", + "+chrome/browser/performance_manager/performance_manager.h", + "+chrome/browser/performance_manager/performance_manager_tab_helper.h", + # Explicitly disallow using SyncMessageFilter to prevent browser from # sending synchronous IPC messages on non-UI threads. "-ipc/ipc_sync_message_filter.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 35f52891..01da6001 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2141,6 +2141,10 @@ SINGLE_VALUE_TYPE( ::switches:: kEnableExperimentalAccessibilityChromeVoxRichTextIndication)}, + {"enable-experimental-kernel-vm-support", + flag_descriptions::kKernelnextVMsName, + flag_descriptions::kKernelnextVMsDescription, kOsCrOS, + FEATURE_VALUE_TYPE(features::kKernelnextVMs)}, #endif // OS_CHROMEOS #if !defined(OS_ANDROID) && defined(GOOGLE_CHROME_BUILD) {"enable-google-branded-context-menu", @@ -2691,11 +2695,6 @@ flag_descriptions::kDirectManipulationStylusDescription, kOsWin | kOsMac | kOsLinux, FEATURE_VALUE_TYPE(features::kDirectManipulationStylus)}, - - {"show-managed-ui", flag_descriptions::kShowManagedUiName, - flag_descriptions::kShowManagedUiDescription, - kOsWin | kOsMac | kOsLinux | kOsCrOS, - FEATURE_VALUE_TYPE(features::kShowManagedUi)}, #endif // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) #if !defined(OS_ANDROID)
diff --git a/chrome/browser/apps/app_service/app_icon_source.cc b/chrome/browser/apps/app_service/app_icon_source.cc index 92b97cd..a919a8e6 100644 --- a/chrome/browser/apps/app_service/app_icon_source.cc +++ b/chrome/browser/apps/app_service/app_icon_source.cc
@@ -52,7 +52,7 @@ AppIconSource::~AppIconSource() = default; -std::string AppIconSource::GetSource() const { +std::string AppIconSource::GetSource() { return chrome::kChromeUIAppIconHost; } @@ -95,18 +95,18 @@ allow_placeholder_icon, base::BindOnce(&RunCallback, callback)); } -std::string AppIconSource::GetMimeType(const std::string&) const { +std::string AppIconSource::GetMimeType(const std::string&) { // We need to explicitly return a mime type, otherwise if the user tries to // drag the image they get no extension. return "image/png"; } -bool AppIconSource::AllowCaching() const { +bool AppIconSource::AllowCaching() { // Should not be cached as caching is performed by proxy. return false; } -bool AppIconSource::ShouldReplaceExistingSource() const { +bool AppIconSource::ShouldReplaceExistingSource() { // The source doesn't maintain its own state so there's no need to replace it. return false; }
diff --git a/chrome/browser/apps/app_service/app_icon_source.h b/chrome/browser/apps/app_service/app_icon_source.h index 9d2a9573..345a33a 100644 --- a/chrome/browser/apps/app_service/app_icon_source.h +++ b/chrome/browser/apps/app_service/app_icon_source.h
@@ -35,14 +35,14 @@ ~AppIconSource() override; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string&) const override; - bool AllowCaching() const override; - bool ShouldReplaceExistingSource() const override; + std::string GetMimeType(const std::string&) override; + bool AllowCaching() override; + bool ShouldReplaceExistingSource() override; private: Profile* profile_;
diff --git a/chrome/browser/chrome_service_worker_browsertest.cc b/chrome/browser/chrome_service_worker_browsertest.cc index f79cb799..0b1527d 100644 --- a/chrome/browser/chrome_service_worker_browsertest.cc +++ b/chrome/browser/chrome_service_worker_browsertest.cc
@@ -719,7 +719,7 @@ ~StaticURLDataSource() override = default; // content::URLDataSource: - std::string GetSource() const override { return source_; } + std::string GetSource() override { return source_; } void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, @@ -727,10 +727,10 @@ std::string data(content_); callback.Run(base::RefCountedString::TakeString(&data)); } - std::string GetMimeType(const std::string& path) const override { + std::string GetMimeType(const std::string& path) override { return "application/javascript"; } - bool ShouldAddContentSecurityPolicy() const override { return false; } + bool ShouldAddContentSecurityPolicy() override { return false; } private: const std::string source_;
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 5842809..a817a83 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -2331,7 +2331,6 @@ "arc/tracing/arc_tracing_model_unittest.cc", "arc/tracing/arc_value_event_unittest.cc", "arc/tts/arc_tts_service_unittest.cc", - "arc/voice_interaction/fake_voice_interaction_controller.cc", "arc/voice_interaction/voice_interaction_controller_client_unittest.cc", "arc/wallpaper/arc_wallpaper_service_unittest.cc", "assistant/assistant_util_unittest.cc",
diff --git a/chrome/browser/chromeos/OWNERS b/chrome/browser/chromeos/OWNERS index 3f4dcc4..41860dd6 100644 --- a/chrome/browser/chromeos/OWNERS +++ b/chrome/browser/chromeos/OWNERS
@@ -4,7 +4,6 @@ achuith@chromium.org alemate@chromium.org -derat@chromium.org jamescook@chromium.org oshima@chromium.org satorux@chromium.org
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_model.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_model.cc index 25f57a2..ca1bc33 100644 --- a/chrome/browser/chromeos/arc/tracing/arc_tracing_model.cc +++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_model.cc
@@ -99,6 +99,9 @@ ArcTracingModel::TracingEvents converted_events; std::map<uint32_t, std::vector<ArcTracingEvent*>> per_thread_pending_events_stack; + + std::map<std::pair<char, std::string>, std::unique_ptr<ArcTracingEvent>> + pending_asynchronous_events; }; bool HandleGraphicsEvent(GraphicsEventsContext* context, @@ -158,6 +161,41 @@ completed_event->SetPhase(TRACE_EVENT_PHASE_COMPLETE); completed_event->SetDuration(timestamp - completed_event->GetTimestamp()); } break; + case TRACE_EVENT_PHASE_ASYNC_BEGIN: + case TRACE_EVENT_PHASE_ASYNC_END: { + const size_t name_pos = ParseUint32(line, event_position + 2, '|', &pid); + if (name_pos == std::string::npos) { + LOG(ERROR) << "Cannot parse pid of trace event: " << line; + return false; + } + const size_t id_pos = line.find('|', name_pos + 2); + if (id_pos == std::string::npos) { + LOG(ERROR) << "Cannot parse name|id of trace event: " << line; + return false; + } + const std::string name = line.substr(name_pos + 1, id_pos - name_pos - 1); + const std::string id = line.substr(id_pos + 1); + std::unique_ptr<ArcTracingEvent> event = + std::make_unique<ArcTracingEvent>(base::DictionaryValue()); + event->SetPhase(phase); + event->SetPid(pid); + event->SetTid(tid); + event->SetTimestamp(timestamp); + event->SetCategory(kAndroidCategory); + event->SetName(name); + // Id here is weak and theoretically can be replicated in another + // processes or for different event names. + const std::string full_id = line.substr(event_position + 2); + event->SetId(id); + if (context->pending_asynchronous_events.find({phase, full_id}) != + context->pending_asynchronous_events.end()) { + LOG(ERROR) << "Found duplicated asynchronous event " << line; + // That could be the real case from Android framework, for example + // animator:opacity trace. Ignore these duplicate events. + return true; + } + context->pending_asynchronous_events[{phase, full_id}] = std::move(event); + } break; default: LOG(ERROR) << "Unsupported type of trace event: " << line; return false; @@ -280,6 +318,15 @@ return true; } +bool SortByTimestampPred(const std::unique_ptr<ArcTracingEvent>& lhs, + const std::unique_ptr<ArcTracingEvent>& rhs) { + const uint64_t lhs_timestamp = lhs->GetTimestamp(); + const uint64_t rhs_timestamp = rhs->GetTimestamp(); + if (lhs_timestamp != rhs_timestamp) + return lhs_timestamp < rhs_timestamp; + return lhs->GetDuration() > rhs->GetDuration(); +} + } // namespace ArcTracingModel::ArcTracingModel() = default; @@ -325,6 +372,11 @@ return false; } + for (auto& group_events : group_events_) { + std::sort(group_events.second.begin(), group_events.second.end(), + SortByTimestampPred); + } + return true; } @@ -362,6 +414,17 @@ return collector; } +ArcTracingModel::TracingEventPtrs ArcTracingModel::GetGroupEvents( + const std::string& id) const { + TracingEventPtrs result; + const auto& it = group_events_.find(id); + if (it == group_events_.end()) + return result; + for (const auto& group_event : it->second) + result.emplace_back(group_event.get()); + return result; +} + bool ArcTracingModel::ProcessEvent(base::ListValue* events) { std::vector<std::unique_ptr<ArcTracingEvent>> parsed_events; for (auto& it : events->GetList()) { @@ -400,14 +463,7 @@ // Events may come by closure that means event started earlier as a root event // for others may appear after children. Sort by ts time. - std::sort(parsed_events.begin(), parsed_events.end(), - [](const auto& lhs, const auto& rhs) { - const uint64_t lhs_timestamp = lhs->GetTimestamp(); - const uint64_t rhs_timestamp = rhs->GetTimestamp(); - if (lhs_timestamp != rhs_timestamp) - return lhs_timestamp < rhs->GetTimestamp(); - return lhs->GetDuration() > rhs->GetDuration(); - }); + std::sort(parsed_events.begin(), parsed_events.end(), SortByTimestampPred); for (auto& event : parsed_events) { switch (event->GetPhase()) { @@ -541,6 +597,12 @@ } } + for (auto& asyncronous_event : + graphics_events_context.pending_asynchronous_events) { + group_events_[asyncronous_event.second->GetId()].emplace_back( + std::move(asyncronous_event.second)); + } + // Close all pending tracing event, assuming last event is 0 duration. for (auto& pending_events : graphics_events_context.per_thread_pending_events_stack) {
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_model.h b/chrome/browser/chromeos/arc/tracing/arc_tracing_model.h index 234f0b7..0e9347a7 100644 --- a/chrome/browser/chromeos/arc/tracing/arc_tracing_model.h +++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_model.h
@@ -54,6 +54,9 @@ TracingEventPtrs Select(const ArcTracingEvent* event, const std::string query) const; + // Gets group of asynchronous events for |id|. + TracingEventPtrs GetGroupEvents(const std::string& id) const; + // Dumps this model to |stream|. void Dump(std::ostream& stream) const;
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_model_unittest.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_model_unittest.cc index 3fe0f3e3..4b6d57c7 100644 --- a/chrome/browser/chromeos/arc/tracing/arc_tracing_model_unittest.cc +++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_model_unittest.cc
@@ -11,6 +11,7 @@ #include "base/json/json_writer.h" #include "base/macros.h" #include "base/path_service.h" +#include "base/trace_event/common/trace_event_common.h" #include "chrome/browser/chromeos/arc/tracing/arc_tracing_event.h" #include "chrome/browser/chromeos/arc/tracing/arc_tracing_event_matcher.h" #include "chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.h" @@ -562,4 +563,43 @@ events.buffer_events()[0][0].type); } +TEST_F(ArcTracingModelTest, AsynchronousSystemEvents) { + base::FilePath base_path; + base::PathService::Get(chrome::DIR_TEST_DATA, &base_path); + const base::FilePath tracing_path = base_path.Append("arc_graphics_tracing") + .Append("trace_async_events.json"); + std::string tracing_data; + base::ReadFileToString(tracing_path, &tracing_data); + DCHECK(!tracing_data.empty()); + + ArcTracingModel model; + ASSERT_TRUE(model.Build(tracing_data)); + + const ArcTracingModel::TracingEventPtrs group1 = model.GetGroupEvents("1"); + const ArcTracingModel::TracingEventPtrs group2 = model.GetGroupEvents("2"); + + constexpr char kAsync1[] = "async1"; + constexpr char kAsync2[] = "async2"; + + ASSERT_EQ(2U, group1.size()); + EXPECT_EQ(kAsync1, group1[0]->GetName()); + EXPECT_EQ(kAsync1, group1[1]->GetName()); + EXPECT_EQ("1", group1[0]->GetId()); + EXPECT_EQ(group1[0]->GetId(), group1[1]->GetId()); + EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_BEGIN, group1[0]->GetPhase()); + EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END, group1[1]->GetPhase()); + EXPECT_EQ(1100000UL, group1[0]->GetTimestamp()); + EXPECT_EQ(1300000UL, group1[1]->GetTimestamp()); + + ASSERT_EQ(2U, group2.size()); + EXPECT_EQ(kAsync2, group2[0]->GetName()); + EXPECT_EQ(kAsync2, group2[1]->GetName()); + EXPECT_EQ("2", group2[0]->GetId()); + EXPECT_EQ(group2[0]->GetId(), group2[1]->GetId()); + EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_BEGIN, group2[0]->GetPhase()); + EXPECT_EQ(TRACE_EVENT_PHASE_ASYNC_END, group2[1]->GetPhase()); + EXPECT_EQ(1200000UL, group2[0]->GetTimestamp()); + EXPECT_EQ(1400000UL, group2[1]->GetTimestamp()); +} + } // namespace arc
diff --git a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc deleted file mode 100644 index 347406d..0000000 --- a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.cc +++ /dev/null
@@ -1,76 +0,0 @@ -// 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/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h" - -namespace arc { - -FakeVoiceInteractionController::FakeVoiceInteractionController() - : binding_(this) {} - -FakeVoiceInteractionController::~FakeVoiceInteractionController() = default; - -ash::mojom::VoiceInteractionControllerPtr -FakeVoiceInteractionController::CreateInterfacePtrAndBind() { - ash::mojom::VoiceInteractionControllerPtr ptr; - binding_.Bind(mojo::MakeRequest(&ptr)); - return ptr; -} - -void FakeVoiceInteractionController::NotifyStatusChanged( - ash::mojom::VoiceInteractionState state) { - voice_interaction_state_ = state; -} - -void FakeVoiceInteractionController::NotifySettingsEnabled(bool enabled) { - voice_interaction_settings_enabled_ = enabled; -} - -void FakeVoiceInteractionController::NotifyContextEnabled(bool enabled) { - voice_interaction_context_enabled_ = enabled; -} - -void FakeVoiceInteractionController::NotifyHotwordAlwaysOn(bool enabled) { - voice_interaction_hotword_always_on_ = enabled; -} - -void FakeVoiceInteractionController::NotifyHotwordEnabled(bool enabled) { - voice_interaction_hotword_enabled_ = enabled; -} - -void FakeVoiceInteractionController::NotifyConsentStatus( - ash::mojom::ConsentStatus consent_status) { - consent_status_ = consent_status; -} - -void FakeVoiceInteractionController::NotifyFeatureAllowed( - ash::mojom::AssistantAllowedState state) { - assistant_allowed_state_ = state; -} - -void FakeVoiceInteractionController::NotifyNotificationEnabled(bool enabled) { - voice_interaction_notification_enabled_ = enabled; -} - -void FakeVoiceInteractionController::NotifyLocaleChanged( - const std::string& locale) { - locale_ = locale; -} - -void FakeVoiceInteractionController::NotifyLaunchWithMicOpen( - bool launch_with_mic_open) { - launch_with_mic_open_ = launch_with_mic_open; -} - -void FakeVoiceInteractionController::NotifyArcPlayStoreEnabledChanged( - bool enabled) { - arc_play_store_enabled_ = enabled; -} - -void FakeVoiceInteractionController::NotifyLockedFullScreenStateChanged( - bool enabled) { - locked_full_screen_enabled_ = enabled; -} - -} // namespace arc
diff --git a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h b/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h deleted file mode 100644 index 1274d37..0000000 --- a/chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h +++ /dev/null
@@ -1,90 +0,0 @@ -// 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_CHROMEOS_ARC_VOICE_INTERACTION_FAKE_VOICE_INTERACTION_CONTROLLER_H_ -#define CHROME_BROWSER_CHROMEOS_ARC_VOICE_INTERACTION_FAKE_VOICE_INTERACTION_CONTROLLER_H_ - -#include <string> - -#include "ash/public/interfaces/voice_interaction_controller.mojom.h" -#include "mojo/public/cpp/bindings/binding.h" - -namespace arc { - -class FakeVoiceInteractionController - : public ash::mojom::VoiceInteractionController { - public: - FakeVoiceInteractionController(); - ~FakeVoiceInteractionController() override; - - ash::mojom::VoiceInteractionControllerPtr CreateInterfacePtrAndBind(); - - // ash::mojom::VoiceInteractionController: - void NotifyStatusChanged(ash::mojom::VoiceInteractionState state) override; - void NotifySettingsEnabled(bool enabled) override; - void NotifyContextEnabled(bool enabled) override; - void NotifyHotwordAlwaysOn(bool enabled) override; - void NotifyHotwordEnabled(bool enabled) override; - void NotifyConsentStatus(ash::mojom::ConsentStatus consent_status) override; - void NotifyFeatureAllowed(ash::mojom::AssistantAllowedState state) override; - void NotifyNotificationEnabled(bool enabled) override; - void NotifyLocaleChanged(const std::string& locale) override; - void NotifyLaunchWithMicOpen(bool launch_with_mic_open) override; - void NotifyArcPlayStoreEnabledChanged(bool enabled) override; - void NotifyLockedFullScreenStateChanged(bool enabled) override; - void AddObserver(ash::mojom::VoiceInteractionObserverPtr observer) override {} - - ash::mojom::VoiceInteractionState voice_interaction_state() const { - return voice_interaction_state_; - } - bool voice_interaction_settings_enabled() const { - return voice_interaction_settings_enabled_; - } - bool voice_interaction_context_enabled() const { - return voice_interaction_context_enabled_; - } - bool voice_interaction_hotword_enabled() const { - return voice_interaction_hotword_enabled_; - } - ash::mojom::ConsentStatus voice_interaction_consent_status() const { - return consent_status_; - } - ash::mojom::AssistantAllowedState assistant_allowed_state() const { - return assistant_allowed_state_; - } - bool voice_interaction_notification_enabled() const { - return voice_interaction_notification_enabled_; - } - const std::string& locale() const { return locale_; } - bool launch_with_mic_open() const { return launch_with_mic_open_; } - bool arc_play_store_enabled() const { return arc_play_store_enabled_; } - bool locked_full_screen_enabled() const { - return locked_full_screen_enabled_; - } - - private: - ash::mojom::VoiceInteractionState voice_interaction_state_ = - ash::mojom::VoiceInteractionState::STOPPED; - bool voice_interaction_settings_enabled_ = false; - bool voice_interaction_context_enabled_ = false; - bool voice_interaction_hotword_always_on_ = false; - bool voice_interaction_hotword_enabled_ = false; - ash::mojom::ConsentStatus consent_status_ = - ash::mojom::ConsentStatus::kUnknown; - bool voice_interaction_notification_enabled_ = false; - std::string locale_; - ash::mojom::AssistantAllowedState assistant_allowed_state_ = - ash::mojom::AssistantAllowedState::DISALLOWED_BY_INCOGNITO; - bool launch_with_mic_open_ = false; - bool arc_play_store_enabled_ = false; - bool locked_full_screen_enabled_ = false; - - mojo::Binding<ash::mojom::VoiceInteractionController> binding_; - - DISALLOW_COPY_AND_ASSIGN(FakeVoiceInteractionController); -}; - -} // namespace arc - -#endif // CHROME_BROWSER_CHROMEOS_ARC_VOICE_INTERACTION_FAKE_VOICE_INTERACTION_CONTROLLER_H_
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc index a3c901e..f1f0f2a 100644 --- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc +++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
@@ -8,7 +8,7 @@ #include <utility> #include "ash/public/cpp/ash_pref_names.h" -#include "ash/public/interfaces/constants.mojom.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "base/bind.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/arc/arc_util.h" @@ -38,7 +38,6 @@ VoiceInteractionControllerClient::VoiceInteractionControllerClient() { DCHECK(!g_voice_interaction_controller_client_instance); - ConnectToVoiceInteractionController(); notification_registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED, @@ -78,57 +77,49 @@ void VoiceInteractionControllerClient::NotifyStatusChanged( ash::mojom::VoiceInteractionState state) { voice_interaction_state_ = state; - voice_interaction_controller_->NotifyStatusChanged(state); + ash::VoiceInteractionController::Get()->NotifyStatusChanged(state); for (auto& observer : observers_) observer.OnStateChanged(state); } void VoiceInteractionControllerClient::NotifyLockedFullScreenStateChanged( bool enabled) { - voice_interaction_controller_->NotifyLockedFullScreenStateChanged(enabled); -} - -void VoiceInteractionControllerClient::SetControllerForTesting( - ash::mojom::VoiceInteractionControllerPtr controller) { - voice_interaction_controller_ = std::move(controller); -} - -void VoiceInteractionControllerClient::FlushMojoForTesting() { - voice_interaction_controller_.FlushForTesting(); + ash::VoiceInteractionController::Get()->NotifyLockedFullScreenStateChanged( + enabled); } void VoiceInteractionControllerClient::NotifySettingsEnabled() { DCHECK(profile_); PrefService* prefs = profile_->GetPrefs(); bool enabled = prefs->GetBoolean(prefs::kVoiceInteractionEnabled); - voice_interaction_controller_->NotifySettingsEnabled(enabled); + ash::VoiceInteractionController::Get()->NotifySettingsEnabled(enabled); } void VoiceInteractionControllerClient::NotifyContextEnabled() { DCHECK(profile_); PrefService* prefs = profile_->GetPrefs(); bool enabled = prefs->GetBoolean(prefs::kVoiceInteractionContextEnabled); - voice_interaction_controller_->NotifyContextEnabled(enabled); + ash::VoiceInteractionController::Get()->NotifyContextEnabled(enabled); } void VoiceInteractionControllerClient::NotifyHotwordEnabled() { DCHECK(profile_); PrefService* prefs = profile_->GetPrefs(); bool enabled = prefs->GetBoolean(prefs::kVoiceInteractionHotwordEnabled); - voice_interaction_controller_->NotifyHotwordEnabled(enabled); + ash::VoiceInteractionController::Get()->NotifyHotwordEnabled(enabled); } void VoiceInteractionControllerClient::NotifyHotwordAlwaysOn() { DCHECK(profile_); PrefService* prefs = profile_->GetPrefs(); bool always_on = prefs->GetBoolean(prefs::kVoiceInteractionHotwordAlwaysOn); - voice_interaction_controller_->NotifyHotwordAlwaysOn(always_on); + ash::VoiceInteractionController::Get()->NotifyHotwordAlwaysOn(always_on); } void VoiceInteractionControllerClient::NotifyConsentStatus() { DCHECK(profile_); PrefService* prefs = profile_->GetPrefs(); - voice_interaction_controller_->NotifyConsentStatus( + ash::VoiceInteractionController::Get()->NotifyConsentStatus( assistant::prefs::GetConsentStatus(prefs)); } @@ -136,14 +127,14 @@ DCHECK(profile_); ash::mojom::AssistantAllowedState state = assistant::IsAssistantAllowedForProfile(profile_); - voice_interaction_controller_->NotifyFeatureAllowed(state); + ash::VoiceInteractionController::Get()->NotifyFeatureAllowed(state); } void VoiceInteractionControllerClient::NotifyNotificationEnabled() { DCHECK(profile_); PrefService* prefs = profile_->GetPrefs(); bool enabled = prefs->GetBoolean(prefs::kVoiceInteractionNotificationEnabled); - voice_interaction_controller_->NotifyNotificationEnabled(enabled); + ash::VoiceInteractionController::Get()->NotifyNotificationEnabled(enabled); } void VoiceInteractionControllerClient::NotifyLocaleChanged() { @@ -154,7 +145,7 @@ std::string out_locale = profile_->GetPrefs()->GetString(language::prefs::kApplicationLocale); - voice_interaction_controller_->NotifyLocaleChanged(out_locale); + ash::VoiceInteractionController::Get()->NotifyLocaleChanged(out_locale); } void VoiceInteractionControllerClient::NotifyLaunchWithMicOpen() { @@ -162,7 +153,8 @@ PrefService* prefs = profile_->GetPrefs(); bool voice_preferred = prefs->GetBoolean(prefs::kVoiceInteractionLaunchWithMicOpen); - voice_interaction_controller_->NotifyLaunchWithMicOpen(voice_preferred); + ash::VoiceInteractionController::Get()->NotifyLaunchWithMicOpen( + voice_preferred); } void VoiceInteractionControllerClient::ActiveUserChanged( @@ -266,18 +258,10 @@ } } -void VoiceInteractionControllerClient::ConnectToVoiceInteractionController() { - content::ServiceManagerConnection* connection = - content::ServiceManagerConnection::GetForProcess(); - // Tests may bind to their own VoiceInteractionController later. - if (connection) - connection->GetConnector()->BindInterface(ash::mojom::kServiceName, - &voice_interaction_controller_); -} - void VoiceInteractionControllerClient::OnArcPlayStoreEnabledChanged( bool enabled) { - voice_interaction_controller_->NotifyArcPlayStoreEnabledChanged(enabled); + ash::VoiceInteractionController::Get()->NotifyArcPlayStoreEnabledChanged( + enabled); } } // namespace arc
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h index ee50a73..d0dd3ab4 100644 --- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h +++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
@@ -46,11 +46,6 @@ return voice_interaction_state_; } - // Testing methods. - void SetControllerForTesting( - ash::mojom::VoiceInteractionControllerPtr controller); - void FlushMojoForTesting(); - private: friend class VoiceInteractionControllerClientTest; @@ -78,10 +73,6 @@ void SetProfile(Profile* profile); - void ConnectToVoiceInteractionController(); - - ash::mojom::VoiceInteractionControllerPtr voice_interaction_controller_; - content::NotificationRegistrar notification_registrar_; std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_; std::unique_ptr<user_manager::ScopedUserSessionStateObserver>
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc index 5287eec..02ea954 100644 --- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc +++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
@@ -4,11 +4,10 @@ #include "chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h" -#include "ash/shell.h" +#include "ash/public/cpp/voice_interaction_controller.h" #include "base/bind.h" #include "base/files/scoped_temp_dir.h" #include "chrome/browser/chromeos/arc/arc_session_manager.h" -#include "chrome/browser/chromeos/arc/voice_interaction/fake_voice_interaction_controller.h" #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" #include "chrome/browser/ui/ash/assistant/assistant_pref_util.h" #include "chrome/test/base/chrome_ash_test_base.h" @@ -49,18 +48,12 @@ GetFakeUserManager()->AddUser(account_id); GetFakeUserManager()->LoginUser(account_id); - voice_interaction_controller_ = - std::make_unique<FakeVoiceInteractionController>(); - voice_interaction_controller_client_ = std::make_unique<VoiceInteractionControllerClient>(); - voice_interaction_controller_client_->SetControllerForTesting( - voice_interaction_controller_->CreateInterfacePtrAndBind()); voice_interaction_controller_client_->SetProfile(profile_.get()); } void TearDown() override { - voice_interaction_controller_.reset(); voice_interaction_controller_client_.reset(); profile_.reset(); arc_session_manager_->Shutdown(); @@ -68,10 +61,6 @@ ChromeAshTestBase::TearDown(); } - FakeVoiceInteractionController* voice_interaction_controller() { - return voice_interaction_controller_.get(); - } - VoiceInteractionControllerClient* voice_interaction_controller_client() { return voice_interaction_controller_client_.get(); } @@ -82,10 +71,6 @@ return arc_session_manager_.get(); } - void FlushVoiceInteractionControllerMojo() { - voice_interaction_controller_client()->FlushMojoForTesting(); - } - private: chromeos::FakeChromeUserManager* GetFakeUserManager() const { return static_cast<chromeos::FakeChromeUserManager*>( @@ -96,7 +81,6 @@ std::unique_ptr<TestingProfile> profile_; user_manager::ScopedUserManager fake_user_manager_; std::unique_ptr<ArcSessionManager> arc_session_manager_; - std::unique_ptr<FakeVoiceInteractionController> voice_interaction_controller_; std::unique_ptr<VoiceInteractionControllerClient> voice_interaction_controller_client_; }; @@ -107,26 +91,17 @@ ASSERT_EQ(false, prefs->GetBoolean(prefs::kVoiceInteractionEnabled)); prefs->SetBoolean(prefs::kVoiceInteractionEnabled, true); ASSERT_EQ(true, prefs->GetBoolean(prefs::kVoiceInteractionEnabled)); - voice_interaction_controller_client()->FlushMojoForTesting(); - EXPECT_EQ( - true, - voice_interaction_controller()->voice_interaction_settings_enabled()); + EXPECT_EQ(true, ash::VoiceInteractionController::Get()->settings_enabled()); ASSERT_EQ(false, prefs->GetBoolean(prefs::kVoiceInteractionContextEnabled)); prefs->SetBoolean(prefs::kVoiceInteractionContextEnabled, true); ASSERT_EQ(true, prefs->GetBoolean(prefs::kVoiceInteractionContextEnabled)); - voice_interaction_controller_client()->FlushMojoForTesting(); - EXPECT_EQ( - true, - voice_interaction_controller()->voice_interaction_context_enabled()); + EXPECT_EQ(true, ash::VoiceInteractionController::Get()->context_enabled()); ASSERT_EQ(false, prefs->GetBoolean(prefs::kVoiceInteractionHotwordEnabled)); prefs->SetBoolean(prefs::kVoiceInteractionHotwordEnabled, true); ASSERT_EQ(true, prefs->GetBoolean(prefs::kVoiceInteractionHotwordEnabled)); - voice_interaction_controller_client()->FlushMojoForTesting(); - EXPECT_EQ( - true, - voice_interaction_controller()->voice_interaction_hotword_enabled()); + EXPECT_EQ(true, ash::VoiceInteractionController::Get()->hotword_enabled()); // Default setting is true. ASSERT_EQ(true, @@ -134,10 +109,8 @@ prefs->SetBoolean(prefs::kVoiceInteractionNotificationEnabled, false); ASSERT_EQ(false, prefs->GetBoolean(prefs::kVoiceInteractionNotificationEnabled)); - voice_interaction_controller_client()->FlushMojoForTesting(); - EXPECT_EQ( - false, - voice_interaction_controller()->voice_interaction_notification_enabled()); + EXPECT_EQ(false, + ash::VoiceInteractionController::Get()->notification_enabled()); ASSERT_EQ(static_cast<int>(ash::mojom::ConsentStatus::kUnknown), prefs->GetInteger(assistant::prefs::kAssistantConsentStatus)); @@ -147,22 +120,20 @@ ASSERT_EQ( static_cast<int>(ash::mojom::ConsentStatus::kActivityControlAccepted), prefs->GetInteger(assistant::prefs::kAssistantConsentStatus)); - voice_interaction_controller_client()->FlushMojoForTesting(); EXPECT_EQ(ash::mojom::ConsentStatus::kActivityControlAccepted, - voice_interaction_controller()->voice_interaction_consent_status()); + ash::VoiceInteractionController::Get()->consent_status()); ASSERT_EQ("", prefs->GetString(language::prefs::kApplicationLocale)); prefs->SetString(language::prefs::kApplicationLocale, "en-CA"); ASSERT_EQ("en-CA", prefs->GetString(language::prefs::kApplicationLocale)); - voice_interaction_controller_client()->FlushMojoForTesting(); - EXPECT_EQ("en-CA", voice_interaction_controller()->locale()); + EXPECT_EQ("en-CA", ash::VoiceInteractionController::Get()->locale()); ASSERT_EQ(false, prefs->GetBoolean(prefs::kVoiceInteractionLaunchWithMicOpen)); prefs->SetBoolean(prefs::kVoiceInteractionLaunchWithMicOpen, true); ASSERT_EQ(true, prefs->GetBoolean(prefs::kVoiceInteractionLaunchWithMicOpen)); - voice_interaction_controller_client()->FlushMojoForTesting(); - EXPECT_EQ(true, voice_interaction_controller()->launch_with_mic_open()); + EXPECT_EQ(true, + ash::VoiceInteractionController::Get()->launch_with_mic_open()); } } // namespace arc
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc index b332d3c..d634079 100644 --- a/chrome/browser/chromeos/crostini/crostini_util.cc +++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -35,6 +35,7 @@ #include "chrome/common/chrome_features.h" #include "chrome/grit/generated_resources.h" #include "chromeos/constants/chromeos_features.h" +#include "chromeos/constants/chromeos_switches.h" #include "components/prefs/pref_service.h" #include "google_apis/gaia/gaia_auth_util.h" #include "ui/base/l10n/l10n_util.h" @@ -213,6 +214,17 @@ return false; } + bool kernelnext = base::CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kKernelnextRestrictVMs); + bool kernelnext_override = + base::FeatureList::IsEnabled(features::kKernelnextVMs); + if (kernelnext && !kernelnext_override) { + // The host kernel is on an experimental version. In future updates this + // device may not have VM support, so we allow enabling VMs, but guard them + // on a chrome://flags switch (enable-experimental-kernel-vm-support). + return false; + } + return base::FeatureList::IsEnabled(features::kCrostini); }
diff --git a/chrome/browser/chromeos/extensions/autotest_private/OWNERS b/chrome/browser/chromeos/extensions/autotest_private/OWNERS index c345b9b..eb940ade 100644 --- a/chrome/browser/chromeos/extensions/autotest_private/OWNERS +++ b/chrome/browser/chromeos/extensions/autotest_private/OWNERS
@@ -1,3 +1,2 @@ achuith@chromium.org -derat@chromium.org stevenjb@chromium.org
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc b/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc index 07cf5015..bd19f8b 100644 --- a/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc +++ b/chrome/browser/chromeos/login/signin/oauth2_login_verifier.cc
@@ -7,7 +7,6 @@ #include <vector> #include "base/logging.h" -#include "components/signin/core/browser/gaia_cookie_manager_service.h" #include "content/public/browser/browser_thread.h" #include "services/identity/public/cpp/accounts_cookie_mutator.h"
diff --git a/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc b/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc index a1802d8b..f125acf 100644 --- a/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc +++ b/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
@@ -48,6 +48,7 @@ constexpr char kCRDResponseConnect[] = "connectResponse"; constexpr char kCRDStateChanged[] = "hostStateChanged"; constexpr char kCRDResponseDisconnect[] = "disconnectResponse"; +constexpr char kCRDResponseError[] = "error"; // Connect message parameters: constexpr char kCRDConnectUserName[] = "userName"; @@ -359,7 +360,7 @@ } else if (type == kCRDResponseDisconnect) { OnDisconnectResponse(); return; - } else if (type == kCRDStateChanged) { + } else if (type == kCRDStateChanged || type == kCRDResponseError) { // Handle CRD host state changes auto* state_value = message_value->FindKeyOfType(kCRDStateKey, base::Value::Type::STRING); @@ -385,7 +386,7 @@ } return; } - LOG(WARNING) << "Unknown message type :" << type; + LOG(WARNING) << "Unknown message type: " << type; } void CRDHostDelegate::OnHelloResponse() {
diff --git a/chrome/browser/chromeos/power/ml/OWNERS b/chrome/browser/chromeos/power/ml/OWNERS index 18f8873..0b16b90 100644 --- a/chrome/browser/chromeos/power/ml/OWNERS +++ b/chrome/browser/chromeos/power/ml/OWNERS
@@ -1,4 +1,3 @@ amoylan@chromium.org -derat@chromium.org jiameng@chromium.org martis@chromium.org
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc index c314e103..53b4989 100644 --- a/chrome/browser/devtools/devtools_sanity_browsertest.cc +++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -2116,7 +2116,7 @@ ~StaticURLDataSource() override = default; // content::URLDataSource: - std::string GetSource() const override { return source_; } + std::string GetSource() override { return source_; } void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, @@ -2124,10 +2124,10 @@ std::string data(content_); callback.Run(base::RefCountedString::TakeString(&data)); } - std::string GetMimeType(const std::string& path) const override { + std::string GetMimeType(const std::string& path) override { return "text/html"; } - bool ShouldAddContentSecurityPolicy() const override { return false; } + bool ShouldAddContentSecurityPolicy() override { return false; } private: const std::string source_;
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc index 2aa77d1..f5b38d07 100644 --- a/chrome/browser/download/download_browsertest.cc +++ b/chrome/browser/download/download_browsertest.cc
@@ -121,6 +121,7 @@ #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_response.h" +#include "net/test/embedded_test_server/request_handler_util.h" #include "net/test/url_request/url_request_mock_http_job.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "services/device/public/mojom/constants.mojom.h" @@ -207,15 +208,14 @@ class OnCanDownloadDecidedObserver { public: - OnCanDownloadDecidedObserver() - : on_decided_called_(false), last_allow_(false) {} + OnCanDownloadDecidedObserver() = default; - void Wait(bool expectation) { - if (on_decided_called_) { - EXPECT_EQ(last_allow_, expectation); - on_decided_called_ = false; + void Wait(const std::vector<bool>& expected_decisions) { + if (expected_decisions.size() <= decisions_.size()) { + EXPECT_TRUE(std::equal(expected_decisions.begin(), + expected_decisions.end(), decisions_.begin())); } else { - expectation_ = expectation; + expected_decisions_ = expected_decisions; base::RunLoop run_loop; completion_closure_ = run_loop.QuitClosure(); run_loop.Run(); @@ -223,22 +223,24 @@ } void OnCanDownloadDecided(bool allow) { - // It is possible this is called before Wait(), so the result needs to - // be stored in that case. - if (!completion_closure_.is_null()) { + decisions_.push_back(allow); + if (decisions_.size() == expected_decisions_.size()) { + DCHECK(!completion_closure_.is_null()); + EXPECT_EQ(decisions_, expected_decisions_); std::move(completion_closure_).Run(); - EXPECT_EQ(allow, expectation_); - } else { - on_decided_called_ = true; - last_allow_ = allow; } } + void Reset() { + decisions_.clear(); + expected_decisions_.clear(); + completion_closure_.Reset(); + } + private: - bool expectation_; + std::vector<bool> decisions_; + std::vector<bool> expected_decisions_; base::Closure completion_closure_; - bool on_decided_called_; - bool last_allow_; DISALLOW_COPY_AND_ASSIGN(OnCanDownloadDecidedObserver); }; @@ -1483,7 +1485,7 @@ } // Test to make sure 'download' attribute in anchor tag doesn't trigger a -// downloadd if DownloadRequestLimiter disallows it. +// download if DownloadRequestLimiter disallows it. IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadRequestLimiterDisallowsAnchorDownloadTag) { OnCanDownloadDecidedObserver can_download_observer; @@ -1515,7 +1517,8 @@ "window.domAutomationController.send(startDownload1());", &download_attempted)); ASSERT_TRUE(download_attempted); - can_download_observer.Wait(false); + can_download_observer.Wait({false}); + can_download_observer.Reset(); // Let the 2nd download to succeed. std::unique_ptr<content::DownloadTestObserver> observer( @@ -1527,7 +1530,7 @@ "window.domAutomationController.send(startDownload2());", &download_attempted)); ASSERT_TRUE(download_attempted); - can_download_observer.Wait(true); + can_download_observer.Wait({true}); // Waits for the 2nd download to complete. observer->WaitForFinished(); @@ -3256,6 +3259,52 @@ browser()->tab_strip_model()->GetActiveWebContents()->Close(); } +// Test the scenario for 3 consecutive <a download> download attempts that all +// trigger a x-origin redirect to another download. No download is expected to +// happen. +IN_PROC_BROWSER_TEST_F( + DownloadTest, + MultipleAnchorDownloadsRequestsCrossOriginRedirectToAnotherDownload) { + std::unique_ptr<content::DownloadTestObserver> downloads_observer( + CreateWaiter(browser(), 0u)); + + OnCanDownloadDecidedObserver can_download_observer; + g_browser_process->download_request_limiter() + ->SetOnCanDownloadDecidedCallbackForTesting(base::BindRepeating( + &OnCanDownloadDecidedObserver::OnCanDownloadDecided, + base::Unretained(&can_download_observer))); + + embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url = embedded_test_server()->GetURL( + "/downloads/multiple_a_download_x_origin_redirect_to_download.html"); + + base::StringPairs port_replacement; + port_replacement.push_back(std::make_pair( + "{{PORT}}", base::NumberToString(embedded_test_server()->port()))); + std::string download_url = net::test_server::GetFilePathWithReplacements( + "redirect_x_origin_download.html", port_replacement); + + url = GURL(url.spec() + "?download_url=" + download_url); + + // Navigate to a page that triggers 3 consecutive <a download> download + // attempts that all trigger a x-origin redirect to another download. + ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 1); + + // The 1st <a download> attempt should pass the download limiter check, + // and prevent further download attempts from passing. The subsequent 2nd/3rd + // <a download> attempts as well as the |download as a result of the x-origin + // redirect from the 1st download attempt| should all fail the download + // limiter check. + can_download_observer.Wait({true, false, false, false}); + + // Only the 1st <a download> attempt passed the download limiter check, but it + // was still aborted by a x-origin redirect, therefore we expect no download + // to happen. + EXPECT_EQ( + 0u, downloads_observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); +} + IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadTest_Renaming) { embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/chrome/browser/download/download_request_limiter.cc b/chrome/browser/download/download_request_limiter.cc index 75cb0fa..379d7d2 100644 --- a/chrome/browser/download/download_request_limiter.cc +++ b/chrome/browser/download/download_request_limiter.cc
@@ -151,9 +151,11 @@ return; } - // If this is a forward/back navigation, also don't reset a prompting or - // blocking limiter state unless a new host is encounted. This prevents a - // page to use history forward/backward to trigger multiple downloads. + // If this is a navigation as a result of x-origin redirect from a previous + // <a download> download, or if this is a forward/back navigation in a host + // already seen, also don't reset a prompting or blocking limiter state. + // This prevents a page to use any of those mechanisms to trigger multiple + // downloads. if (IsNavigationRestricted(navigation_handle)) return; } @@ -418,6 +420,9 @@ bool DownloadRequestLimiter::TabDownloadState::IsNavigationRestricted( content::NavigationHandle* navigation_handle) { + if (navigation_handle->FromDownloadCrossOriginRedirect()) + return true; + std::string host = navigation_handle->GetURL().host(); if (navigation_handle->GetPageTransition() & ui::PAGE_TRANSITION_FORWARD_BACK) return restricted_hosts_.find(host) != restricted_hosts_.end(); @@ -585,6 +590,7 @@ case CONTENT_SETTING_ASK: state->PromptUserForDownload(std::move(callback)); state->increment_download_count(); + ret = false; break; case CONTENT_SETTING_SESSION_ONLY: case CONTENT_SETTING_NUM_SETTINGS:
diff --git a/chrome/browser/download/download_request_limiter.h b/chrome/browser/download/download_request_limiter.h index 030f03e..9b00b28 100644 --- a/chrome/browser/download/download_request_limiter.h +++ b/chrome/browser/download/download_request_limiter.h
@@ -234,6 +234,9 @@ FRIEND_TEST_ALL_PREFIXES(DownloadTest, DownloadResourceThrottleCancels); FRIEND_TEST_ALL_PREFIXES(DownloadTest, DownloadRequestLimiterDisallowsAnchorDownloadTag); + FRIEND_TEST_ALL_PREFIXES( + DownloadTest, + MultipleAnchorDownloadsRequestsCrossOriginRedirectToAnotherDownload); FRIEND_TEST_ALL_PREFIXES(ContentSettingBubbleControllerTest, Init); FRIEND_TEST_ALL_PREFIXES(ContentSettingImageModelBrowserTest, CreateBubbleModel);
diff --git a/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc b/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc index 5e1ea66b..5556209 100644 --- a/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc +++ b/chrome/browser/extensions/api/feedback_private/feedback_browsertest.cc
@@ -10,6 +10,8 @@ #include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/feedback/feedback_dialog_utils.h" +#include "chrome/browser/feedback/feedback_uploader_chrome.h" +#include "chrome/browser/feedback/feedback_uploader_factory_chrome.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/extensions/extension_constants.h" @@ -94,6 +96,18 @@ } }; +class TestFeedbackUploaderDelegate + : public feedback::FeedbackUploaderChrome::Delegate { + public: + explicit TestFeedbackUploaderDelegate(base::RunLoop* quit_on_dispatch) + : quit_on_dispatch_(quit_on_dispatch) {} + + void OnStartDispatchingReport() override { quit_on_dispatch_->Quit(); } + + private: + base::RunLoop* quit_on_dispatch_; +}; + // Disabled for ASan due to flakiness on Mac ASan 64 Tests (1). // See crbug.com/757243. #if defined(ADDRESS_SANITIZER) @@ -303,7 +317,7 @@ EXPECT_TRUE(bool_result); } #endif // if defined(CHROME_OS) - + IN_PROC_BROWSER_TEST_F(FeedbackTest, GetTargetTabUrl) { const std::pair<std::string, std::string> test_cases[] = { {"https://www.google.com/", "https://www.google.com/"}, @@ -340,4 +354,49 @@ DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window); } } + +IN_PROC_BROWSER_TEST_F(FeedbackTest, SubmissionTest) { + WaitForExtensionViewsToLoad(); + + ASSERT_TRUE(IsFeedbackAppAvailable()); + StartFeedbackUI(FeedbackFlow::FEEDBACK_FLOW_GOOGLEINTERNAL, std::string()); + VerifyFeedbackAppLaunch(); + + AppWindow* const window = + PlatformAppBrowserTest::GetFirstAppWindowForBrowser(browser()); + ASSERT_TRUE(window); + content::WebContents* const content = window->web_contents(); + + // Set a delegate for the uploader which will be invoked when the report + // normally would have been uploaded. We have it setup to then quit the + // RunLoop which will then allow us to terminate. + base::RunLoop run_loop; + TestFeedbackUploaderDelegate delegate(&run_loop); + feedback::FeedbackUploaderFactoryChrome::GetInstance() + ->GetForBrowserContext(browser()->profile()) + ->set_feedback_uploader_delegate(&delegate); + + // Click the send button. + bool bool_result = false; + ASSERT_TRUE(content::ExecuteScriptAndExtractBool( + content, + "domAutomationController.send(" + " ((function() {" + " if ($('send-report-button') != null) {" + " document.getElementById('send-report-button').click();" + " return true;" + " }" + " return false;" + " })()));", + &bool_result)); + EXPECT_TRUE(bool_result); + + // This will DCHECK if the JS private API call doesn't return a value, which + // is the main case we are concerned about. + run_loop.Run(); + feedback::FeedbackUploaderFactoryChrome::GetInstance() + ->GetForBrowserContext(browser()->profile()) + ->set_feedback_uploader_delegate(nullptr); +} + } // namespace extensions
diff --git a/chrome/browser/extensions/process_manager_browsertest.cc b/chrome/browser/extensions/process_manager_browsertest.cc index ff003385..cd2a23d5 100644 --- a/chrome/browser/extensions/process_manager_browsertest.cc +++ b/chrome/browser/extensions/process_manager_browsertest.cc
@@ -915,14 +915,6 @@ } } -// Flaky on Win, Mac and Linux (http://crbug.com/806684). -#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) -#define MAYBE_NestedURLDownloadsToExtensionAllowed \ - DISABLED_NestedURLDownloadsToExtensionAllowed -#else -#define MAYBE_NestedURLDownloadsToExtensionAllowed \ - NestedURLDownloadsToExtensionAllowed -#endif // Check that browser-side restrictions on extension blob/filesystem URLs allow // navigations that will result in downloads. See https://crbug.com/714373. IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest,
diff --git a/chrome/browser/feedback/feedback_uploader_chrome.cc b/chrome/browser/feedback/feedback_uploader_chrome.cc index 37653a1d..f0f4f14 100644 --- a/chrome/browser/feedback/feedback_uploader_chrome.cc +++ b/chrome/browser/feedback/feedback_uploader_chrome.cc
@@ -46,6 +46,9 @@ } void FeedbackUploaderChrome::StartDispatchingReport() { + if (delegate_) + delegate_->OnStartDispatchingReport(); + access_token_.clear(); // TODO(crbug.com/849591): Instead of getting the IdentityManager from the
diff --git a/chrome/browser/feedback/feedback_uploader_chrome.h b/chrome/browser/feedback/feedback_uploader_chrome.h index e26eaf0..5a49d651 100644 --- a/chrome/browser/feedback/feedback_uploader_chrome.h +++ b/chrome/browser/feedback/feedback_uploader_chrome.h
@@ -28,6 +28,19 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner); ~FeedbackUploaderChrome() override; + class Delegate { + public: + // Notifies the delegate when we have started dispatching a feedback report. + virtual void OnStartDispatchingReport() = 0; + + protected: + virtual ~Delegate() = default; + }; + + void set_feedback_uploader_delegate(Delegate* delegate) { + delegate_ = delegate; + } + private: // feedback::FeedbackUploader: void StartDispatchingReport() override; @@ -41,6 +54,8 @@ std::string access_token_; + Delegate* delegate_ = nullptr; // Not owned. + DISALLOW_COPY_AND_ASSIGN(FeedbackUploaderChrome); };
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 5e8395e..46ab16d 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -1066,6 +1066,11 @@ "expiry_milestone": 78 }, { + "name": "enable-experimental-kernel-vm-support", + "owners": [ "jflat", "zwisler" ], + "expiry_milestone": 78 + }, + { "name": "enable-experimental-productivity-features", "owners": [ "feature-control@chromium.org" ], "expiry_milestone": 76 @@ -2195,8 +2200,12 @@ }, { "name": "media-router-cast-allow-all-ips", - // "owners": [ "your-team" ], - "expiry_milestone": 76 + "owners": [ "mfoltz" ], + // This flag is used by users with unusual network configurations to allow + // cast to work, but enabling this behavior has security implications that + // aren't easily understood. It is primarily used by support to help + // individual users get cast working. + "expiry_milestone": -1 }, { "name": "memlog", @@ -2717,11 +2726,6 @@ "expiry_milestone": -1 }, { - "name": "show-managed-ui", - // "owners": [ "your-team" ], - "expiry_milestone": 76 - }, - { "name": "show-taps", "owners": [ "//ash/OWNERS" ], // This is a debug flag, so that video bug reports can show input taps to
diff --git a/chrome/browser/flag-never-expire-list.json b/chrome/browser/flag-never-expire-list.json index df1a9e7..7122317 100644 --- a/chrome/browser/flag-never-expire-list.json +++ b/chrome/browser/flag-never-expire-list.json
@@ -64,6 +64,7 @@ "in-product-help-demo-mode-choice", "list-all-display-modes", "load-media-router-component-extension", + "media-router-cast-allow-all-ips", "memlog", "memlog-sampling-rate", "memlog-stack-mode",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 99f90cc..74a3bb68 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -2586,6 +2586,10 @@ "When going to a site that has URL managable by a PWA, show the intent" "picker to allow user to open the URL in the app."; +const char kKernelnextVMsName[] = "Enable VMs on experimental kernels."; +const char kKernelnextVMsDescription[] = + "Enables VM support on devices running experimental kernel versions."; + const char kOmniboxDriveSuggestionsName[] = "Omnibox Google Drive Document suggestions"; const char kOmniboxDriveSuggestionsDescriptions[] = @@ -2643,11 +2647,6 @@ "Enables proactive tab freezing and discarding. This requires " "#enable-page-almost-idle."; -const char kShowManagedUiName[] = "Show managed UI for managed users"; -const char kShowManagedUiDescription[] = - "Enabled/disable showing enterprise users a 'Managed by your organization' " - "message in the app menu and on some chrome:// pages."; - #if defined(GOOGLE_CHROME_BUILD) const char kGoogleBrandedContextMenuName[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index fb9da327..9ae423c 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -1527,6 +1527,9 @@ extern const char kIntentPickerName[]; extern const char kIntentPickerDescription[]; +extern const char kKernelnextVMsName[]; +extern const char kKernelnextVMsDescription[]; + extern const char kOmniboxDriveSuggestionsName[]; extern const char kOmniboxDriveSuggestionsDescriptions[]; @@ -1554,9 +1557,6 @@ extern const char kProactiveTabFreezeAndDiscardName[]; extern const char kProactiveTabFreezeAndDiscardDescription[]; -extern const char kShowManagedUiName[]; -extern const char kShowManagedUiDescription[]; - #if defined(GOOGLE_CHROME_BUILD) extern const char kGoogleBrandedContextMenuName[];
diff --git a/chrome/browser/infobars/infobars_browsertest.cc b/chrome/browser/infobars/infobars_browsertest.cc index 40cc924..ecce1cd 100644 --- a/chrome/browser/infobars/infobars_browsertest.cc +++ b/chrome/browser/infobars/infobars_browsertest.cc
@@ -570,12 +570,9 @@ ShowAndVerifyUi(); } -// TODO(https://crbug.com/965468) Resource generation fails on Windows. -#if !defined(OS_WIN) IN_PROC_BROWSER_TEST_F(InfoBarUiTest, InvokeUi_tab_sharing) { ShowAndVerifyUi(); } -#endif IN_PROC_BROWSER_TEST_F(InfoBarUiTest, InvokeUi_multiple_infobars) { ShowAndVerifyUi();
diff --git a/chrome/browser/media/router/test/mock_screen_availability_listener.cc b/chrome/browser/media/router/test/mock_screen_availability_listener.cc index 091513b..374ce3e 100644 --- a/chrome/browser/media/router/test/mock_screen_availability_listener.cc +++ b/chrome/browser/media/router/test/mock_screen_availability_listener.cc
@@ -12,7 +12,7 @@ MockScreenAvailabilityListener::~MockScreenAvailabilityListener() {} -GURL MockScreenAvailabilityListener::GetAvailabilityUrl() const { +GURL MockScreenAvailabilityListener::GetAvailabilityUrl() { return availability_url_; }
diff --git a/chrome/browser/media/router/test/mock_screen_availability_listener.h b/chrome/browser/media/router/test/mock_screen_availability_listener.h index ac436af9..e90b5c66 100644 --- a/chrome/browser/media/router/test/mock_screen_availability_listener.h +++ b/chrome/browser/media/router/test/mock_screen_availability_listener.h
@@ -16,7 +16,7 @@ explicit MockScreenAvailabilityListener(const GURL& availability_url); ~MockScreenAvailabilityListener() override; - GURL GetAvailabilityUrl() const override; + GURL GetAvailabilityUrl() override; MOCK_METHOD1(OnScreenAvailabilityChanged, void(blink::mojom::ScreenAvailability));
diff --git a/chrome/browser/metrics/DEPS b/chrome/browser/metrics/DEPS index 2e11ffb..cf0b66cb 100644 --- a/chrome/browser/metrics/DEPS +++ b/chrome/browser/metrics/DEPS
@@ -1,3 +1,10 @@ include_rules = [ "+chrome/services/util_win/util_win_service.h", ] + +specific_include_rules = { + # TODO(siggi): Fix. https://crbug.com/969026 + "process_memory_metrics_emitter.cc": [ + "+chrome/browser/performance_manager", + ], +}
diff --git a/chrome/browser/notifications/scheduler/BUILD.gn b/chrome/browser/notifications/scheduler/BUILD.gn index e749e31..59b3088 100644 --- a/chrome/browser/notifications/scheduler/BUILD.gn +++ b/chrome/browser/notifications/scheduler/BUILD.gn
@@ -14,31 +14,7 @@ ] public_deps = [ - ":public", - ] -} - -source_set("public") { - sources = [ - "notification_background_task_scheduler.h", - "notification_data.cc", - "notification_data.h", - "notification_params.cc", - "notification_params.h", - "notification_schedule_service.h", - "notification_scheduler_client.h", - "notification_scheduler_client_registrar.cc", - "notification_scheduler_client_registrar.h", - "notification_scheduler_types.h", - "schedule_params.cc", - "schedule_params.h", - "user_action_handler.h", - ] - - deps = [ - "//base", - "//components/keyed_service/core", - "//skia", + "//chrome/browser/notifications/scheduler/public", ] } @@ -51,106 +27,18 @@ # This target should not depend on anything in //chrome/* except the proto library. deps = [ - ":lib", - ":public", "//base", "//chrome/browser/notifications/proto", + "//chrome/browser/notifications/scheduler/internal:lib", + "//chrome/browser/notifications/scheduler/public", "//components/keyed_service/core", "//components/leveldb_proto", ] } -# Internal library that embedders should not directly depend on. -source_set("lib") { - visibility = [ - ":factory", - ":scheduler", - ":unit_tests", - "//chrome/browser/notifications/scheduler/test:test_lib", - ] - - sources = [ - "background_task_coordinator.cc", - "background_task_coordinator.h", - "collection_store.h", - "display_decider.cc", - "display_decider.h", - "distribution_policy.cc", - "distribution_policy.h", - "icon_entry.cc", - "icon_entry.h", - "icon_store.cc", - "icon_store.h", - "impression_history_tracker.cc", - "impression_history_tracker.h", - "impression_store.cc", - "impression_store.h", - "impression_types.cc", - "impression_types.h", - "init_aware_scheduler.cc", - "init_aware_scheduler.h", - "notification_entry.cc", - "notification_entry.h", - "notification_schedule_service_impl.cc", - "notification_schedule_service_impl.h", - "notification_scheduler.cc", - "notification_scheduler.h", - "notification_scheduler_context.cc", - "notification_scheduler_context.h", - "notification_store.cc", - "notification_store.h", - "proto_conversion.cc", - "proto_conversion.h", - "scheduled_notification_manager.cc", - "scheduled_notification_manager.h", - "scheduler_config.cc", - "scheduler_config.h", - "scheduler_utils.cc", - "scheduler_utils.h", - ] - - # This target should not depend on anything in //chrome/* except the proto library. - deps = [ - ":public", - "//base", - "//chrome/browser/notifications/proto", - "//components/keyed_service/core", - "//components/leveldb_proto", - "//skia", - ] -} - -source_set("unit_tests") { +group("unit_tests") { testonly = true - sources = [ - "background_task_coordinator_unittest.cc", - "display_decider_unittest.cc", - "distribution_policy_unittest.cc", - "icon_store_unittest.cc", - "impression_history_tracker_unittest.cc", - "impression_store_unittest.cc", - "init_aware_scheduler_unittest.cc", - "notification_store_unittests.cc", - "proto_conversion_unittest.cc", - "scheduled_notification_manager_unittest.cc", - "scheduler_utils_unittest.cc", - ] - deps = [ - ":lib", - ":public", - "//chrome/browser/notifications/proto", - "//chrome/browser/notifications/scheduler/test:test_lib", - "//components/leveldb_proto:test_support", - "//testing/gmock", - "//testing/gtest", + "//chrome/browser/notifications/scheduler/internal:unit_tests", ] } - -if (is_android) { - java_cpp_enum("jni_enums") { - sources = [ - "notification_scheduler_types.h", - ] - } -}
diff --git a/chrome/browser/notifications/scheduler/background_task_coordinator.cc b/chrome/browser/notifications/scheduler/background_task_coordinator.cc deleted file mode 100644 index b31c623..0000000 --- a/chrome/browser/notifications/scheduler/background_task_coordinator.cc +++ /dev/null
@@ -1,165 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/background_task_coordinator.h" - -#include <algorithm> -#include <utility> - -#include "base/numerics/ranges.h" -#include "base/optional.h" -#include "base/time/clock.h" -#include "chrome/browser/notifications/scheduler/impression_types.h" -#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h" -#include "chrome/browser/notifications/scheduler/scheduler_config.h" -#include "chrome/browser/notifications/scheduler/scheduler_utils.h" - -namespace notifications { -namespace { - -class BackgroundTaskCoordinatorHelper { - public: - BackgroundTaskCoordinatorHelper( - NotificationBackgroundTaskScheduler* background_task, - const SchedulerConfig* config, - base::Clock* clock) - : background_task_(background_task), config_(config), clock_(clock) {} - ~BackgroundTaskCoordinatorHelper() = default; - - void ScheduleBackgroundTask( - BackgroundTaskCoordinator::Notifications notifications, - BackgroundTaskCoordinator::ClientStates client_states, - SchedulerTaskTime task_start_time) { - if (notifications.empty()) { - background_task_->Cancel(); - return; - } - - std::map<SchedulerClientType, int> shown_per_type; - int shown_total = 0; - SchedulerClientType last_shown_type = SchedulerClientType::kUnknown; - NotificationsShownToday(client_states, &shown_per_type, &shown_total, - &last_shown_type); - bool reach_max_today_all_type = - shown_total >= config_->max_daily_shown_all_type; - base::Time next_morning = NextMorning(); - base::Time this_evening = ThisEvening(); - - for (const auto& pair : notifications) { - auto type = pair.first; - auto it = client_states.find(type); - if (pair.second.empty() || (it == client_states.end())) - continue; - - const ClientState* client_state = it->second; - - // Try to schedule on the day that suppression expires. - if (client_state->suppression_info.has_value()) { - const auto& suppression = client_state->suppression_info.value(); - base::Time suppression_expire; - ToLocalHour(config_->morning_task_hour, suppression.ReleaseTime(), - 0 /*day_delta*/, &suppression_expire); - MaybeUpdateBackgroundTaskTime( - std::max(suppression_expire, next_morning)); - continue; - } - - // Has met the quota for this notification type or for all types, only can - // send more on next day. - bool reach_max_today = - shown_per_type[type] >= client_state->current_max_daily_show; - if (reach_max_today || reach_max_today_all_type) { - MaybeUpdateBackgroundTaskTime(next_morning); - continue; - } - - switch (task_start_time) { - case SchedulerTaskTime::kMorning: - // Still can send more in the evening. - MaybeUpdateBackgroundTaskTime(this_evening); - break; - case SchedulerTaskTime::kEvening: - // Wait until the next calendar day. - MaybeUpdateBackgroundTaskTime(next_morning); - break; - case SchedulerTaskTime::kUnknown: - // TODO(xingliu): Support arbitrary time background task. - NOTIMPLEMENTED(); - break; - } - } - - ScheduleBackgroundTaskInternal(task_start_time); - } - - private: - // Returns the morning background task time on the next day. - base::Time NextMorning() { - base::Time next_morning; - bool success = ToLocalHour(config_->morning_task_hour, clock_->Now(), - 1 /*day_delta*/, &next_morning); - DCHECK(success); - return next_morning; - } - - // Returns the evening background task time on today. - base::Time ThisEvening() { - base::Time this_evening; - bool success = ToLocalHour(config_->evening_task_hour, clock_->Now(), - 0 /*day_delta*/, &this_evening); - DCHECK(success); - return this_evening; - } - - void MaybeUpdateBackgroundTaskTime(const base::Time& time) { - if (!background_task_time_.has_value() || - time < background_task_time_.value()) - background_task_time_ = time; - } - - void ScheduleBackgroundTaskInternal(SchedulerTaskTime task_start_time) { - if (!background_task_time_.has_value()) - return; - - base::TimeDelta window_start_time = - background_task_time_.value() - clock_->Now(); - window_start_time = base::ClampToRange(window_start_time, base::TimeDelta(), - base::TimeDelta::Max()); - - background_task_->Schedule( - task_start_time, window_start_time, - window_start_time + config_->background_task_window_duration); - } - - NotificationBackgroundTaskScheduler* background_task_; - const SchedulerConfig* config_; - base::Clock* clock_; - base::Optional<base::Time> background_task_time_; - - DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinatorHelper); -}; - -} // namespace - -BackgroundTaskCoordinator::BackgroundTaskCoordinator( - std::unique_ptr<NotificationBackgroundTaskScheduler> background_task, - const SchedulerConfig* config, - base::Clock* clock) - : background_task_(std::move(background_task)), - config_(config), - clock_(clock) {} - -BackgroundTaskCoordinator::~BackgroundTaskCoordinator() = default; - -void BackgroundTaskCoordinator::ScheduleBackgroundTask( - Notifications notifications, - ClientStates client_states, - SchedulerTaskTime task_start_time) { - auto helper = std::make_unique<BackgroundTaskCoordinatorHelper>( - background_task_.get(), config_, clock_); - helper->ScheduleBackgroundTask(std::move(notifications), - std::move(client_states), task_start_time); -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/background_task_coordinator.h b/chrome/browser/notifications/scheduler/background_task_coordinator.h deleted file mode 100644 index 0487485..0000000 --- a/chrome/browser/notifications/scheduler/background_task_coordinator.h +++ /dev/null
@@ -1,59 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_BACKGROUND_TASK_COORDINATOR_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_BACKGROUND_TASK_COORDINATOR_H_ - -#include <map> -#include <memory> -#include <vector> - -#include "base/macros.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" - -namespace base { -class Clock; -} // namespace base - -namespace notifications { - -class NotificationBackgroundTaskScheduler; -struct ClientState; -struct NotificationEntry; -struct SchedulerConfig; - -// Schedules background task at the right time based on scheduled notification -// data and impression data. -class BackgroundTaskCoordinator { - public: - using Notifications = - std::map<SchedulerClientType, std::vector<const NotificationEntry*>>; - using ClientStates = std::map<SchedulerClientType, const ClientState*>; - BackgroundTaskCoordinator( - std::unique_ptr<NotificationBackgroundTaskScheduler> background_task, - const SchedulerConfig* config, - base::Clock* clock); - virtual ~BackgroundTaskCoordinator(); - - // Schedule background task based on current notification in the storage. - virtual void ScheduleBackgroundTask(Notifications notifications, - ClientStates client_states, - SchedulerTaskTime task_start_time); - - private: - // The class that actually schedules platform background task. - std::unique_ptr<NotificationBackgroundTaskScheduler> background_task_; - - // System configuration. - const SchedulerConfig* config_; - - // Clock to query the current timestamp. - base::Clock* clock_; - - DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinator); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_BACKGROUND_TASK_COORDINATOR_H_
diff --git a/chrome/browser/notifications/scheduler/background_task_coordinator_unittest.cc b/chrome/browser/notifications/scheduler/background_task_coordinator_unittest.cc deleted file mode 100644 index c760011c..0000000 --- a/chrome/browser/notifications/scheduler/background_task_coordinator_unittest.cc +++ /dev/null
@@ -1,337 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/background_task_coordinator.h" - -#include <map> -#include <memory> -#include <utility> -#include <vector> - -#include "base/test/scoped_task_environment.h" -#include "base/time/clock.h" -#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h" -#include "chrome/browser/notifications/scheduler/notification_entry.h" -#include "chrome/browser/notifications/scheduler/scheduler_config.h" -#include "chrome/browser/notifications/scheduler/test/test_utils.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::_; - -namespace notifications { -namespace { - -using Notifications = BackgroundTaskCoordinator::Notifications; -using ClientStates = BackgroundTaskCoordinator::ClientStates; - -const char kGuid[] = "1234"; -const std::vector<test::ImpressionTestData> kSingleClientImpressionTestData = { - {SchedulerClientType::kTest1, - 1 /* current_max_daily_show */, - {}, - base::nullopt /* suppression_info */}}; - -const std::vector<test::ImpressionTestData> kClientsImpressionTestData = { - {SchedulerClientType::kTest1, - 1 /* current_max_daily_show */, - {}, - base::nullopt /* suppression_info */}, - {SchedulerClientType::kTest2, - 2 /* current_max_daily_show */, - {}, - base::nullopt /* suppression_info */}}; - -class MockNotificationBackgroundTaskScheduler - : public NotificationBackgroundTaskScheduler { - public: - MockNotificationBackgroundTaskScheduler() = default; - ~MockNotificationBackgroundTaskScheduler() override = default; - MOCK_METHOD3(Schedule, - void(notifications::SchedulerTaskTime, - base::TimeDelta, - base::TimeDelta)); - MOCK_METHOD0(Cancel, void()); - - private: - DISALLOW_COPY_AND_ASSIGN(MockNotificationBackgroundTaskScheduler); -}; - -// Clock to mock Clock::Now() to get a fixed time in the test. -class FakeClock : public base::Clock { - public: - FakeClock() = default; - ~FakeClock() override = default; - - void SetTime(const base::Time& time) { time_ = time; } - - private: - // base::Clock implementation. - base::Time Now() const override { return time_; } - - base::Time time_; - DISALLOW_COPY_AND_ASSIGN(FakeClock); -}; - -struct TestData { - // Impression data as the input. - std::vector<test::ImpressionTestData> impression_test_data; - - // Notification entries as the input. - std::vector<NotificationEntry> notification_entries; - - // The type of current background task. - SchedulerTaskTime task_start_time = SchedulerTaskTime::kMorning; -}; - -class BackgroundTaskCoordinatorTest : public testing::Test { - public: - BackgroundTaskCoordinatorTest() = default; - ~BackgroundTaskCoordinatorTest() override = default; - - protected: - void SetUp() override { - // Setup configuration used by this test. - config_.morning_task_hour = 6; - config_.evening_task_hour = 18; - config_.max_daily_shown_all_type = 3; - config_.max_daily_shown_per_type = 2; - config_.suppression_duration = base::TimeDelta::FromDays(3); - - auto background_task = - std::make_unique<MockNotificationBackgroundTaskScheduler>(); - background_task_ = background_task.get(); - coordinator_ = std::make_unique<BackgroundTaskCoordinator>( - std::move(background_task), &config_, &clock_); - } - - MockNotificationBackgroundTaskScheduler* background_task() { - return background_task_; - } - - SchedulerConfig* config() { return &config_; } - - void SetNow(const char* now_str) { - base::Time now = GetTime(now_str); - clock_.SetTime(now); - } - - base::Time GetTime(const char* time_str) { - base::Time time; - bool success = base::Time::FromString(time_str, &time); - DCHECK(success); - return time; - } - - void ScheduleTask(const TestData& test_data) { - test_data_ = test_data; - test::AddImpressionTestData(test_data_.impression_test_data, - &client_states_); - std::map<SchedulerClientType, const ClientState*> client_states; - for (const auto& type : client_states_) { - client_states.emplace(type.first, type.second.get()); - } - - Notifications notifications; - for (const auto& entry : test_data_.notification_entries) { - notifications[entry.type].emplace_back(&entry); - } - coordinator_->ScheduleBackgroundTask(std::move(notifications), - std::move(client_states), - test_data_.task_start_time); - } - - private: - base::test::ScopedTaskEnvironment scoped_task_environment_; - FakeClock clock_; - SchedulerConfig config_; - std::unique_ptr<BackgroundTaskCoordinator> coordinator_; - MockNotificationBackgroundTaskScheduler* background_task_; - TestData test_data_; - std::map<SchedulerClientType, std::unique_ptr<ClientState>> client_states_; - - DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinatorTest); -}; - -// No notification persisted, then no background task needs to be scheduled. -// And current task should be canceled. -TEST_F(BackgroundTaskCoordinatorTest, NoNotification) { - EXPECT_CALL(*background_task(), Cancel()); - EXPECT_CALL(*background_task(), Schedule(_, _, _)).Times(0); - TestData test_data; - test_data.impression_test_data = kSingleClientImpressionTestData; - ScheduleTask(test_data); -} - -// In a morning task, find one notification and schedule an evening task. -TEST_F(BackgroundTaskCoordinatorTest, InMorningScheduleEvening) { - const char kNow[] = "04/25/20 01:00:00 AM"; - SetNow(kNow); - EXPECT_CALL(*background_task(), Cancel()).Times(0); - // Expected to run task this evening. - auto expected_window_start = GetTime("04/25/20 18:00:00 PM") - GetTime(kNow); - EXPECT_CALL(*background_task(), - Schedule(_, expected_window_start, - expected_window_start + - config()->background_task_window_duration)); - - NotificationEntry entry(SchedulerClientType::kTest1, kGuid); - TestData test_data{ - kSingleClientImpressionTestData, {entry}, SchedulerTaskTime::kMorning}; - ScheduleTask(test_data); -} - -// In morning task, schedule evening task but throttled, schedule to next -// morning. -TEST_F(BackgroundTaskCoordinatorTest, InMorningScheduleEveningThrottled) { - const char kNow[] = "04/25/20 02:00:00 PM"; - SetNow(kNow); - - EXPECT_CALL(*background_task(), Cancel()).Times(0); - // Expected to run task next morning. - EXPECT_CALL(*background_task(), - Schedule(_, GetTime("04/26/20 06:00:00 AM") - GetTime(kNow), _)); - - auto impression_data = kSingleClientImpressionTestData; - Impression impression; - impression.create_time = GetTime("04/25/20 01:00:00 AM"); - impression_data.back().impressions.emplace_back(impression); - - NotificationEntry entry(SchedulerClientType::kTest1, kGuid); - TestData test_data{impression_data, {entry}, SchedulerTaskTime::kMorning}; - ScheduleTask(test_data); -} - -// In an evening task, schedule background task to run next morning. -TEST_F(BackgroundTaskCoordinatorTest, InEveningScheduleNextMorning) { - const char kNow[] = "04/25/20 18:00:00 PM"; - SetNow(kNow); - EXPECT_CALL(*background_task(), Cancel()).Times(0); - // Expected to run task next morning. - auto expected_window_start = GetTime("04/26/20 06:00:00 AM") - GetTime(kNow); - EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _)); - - NotificationEntry entry(SchedulerClientType::kTest1, kGuid); - TestData test_data{ - kSingleClientImpressionTestData, {entry}, SchedulerTaskTime::kEvening}; - ScheduleTask(test_data); -} - -// In an evening task, schedule background task to run next morning, even if we -// reached the daily max. -TEST_F(BackgroundTaskCoordinatorTest, InEveningScheduleNextMorningThrottled) { - const char kNow[] = "04/25/20 18:00:00 PM"; - SetNow(kNow); - EXPECT_CALL(*background_task(), Cancel()).Times(0); - // Expected to run task next morning. - auto expected_window_start = GetTime("04/26/20 06:00:00 AM") - GetTime(kNow); - EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _)); - - // We have reached daily max. - auto impression_data = kSingleClientImpressionTestData; - Impression impression; - impression.create_time = GetTime("04/25/20 01:00:00 AM"); - impression_data.back().impressions.emplace_back(impression); - - NotificationEntry entry(SchedulerClientType::kTest1, kGuid); - TestData test_data{ - kSingleClientImpressionTestData, {entry}, SchedulerTaskTime::kEvening}; - ScheduleTask(test_data); -} - -// Suppression will result in background task scheduled after suppression -// expired. -TEST_F(BackgroundTaskCoordinatorTest, Suppression) { - const char kNow[] = "04/25/20 06:00:00 AM"; - SetNow(kNow); - EXPECT_CALL(*background_task(), Cancel()).Times(0); - // Expected to run task in the morning after suppression expired. - auto expected_window_start = GetTime("04/28/20 06:00:00 AM") - GetTime(kNow); - EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _)); - - auto impression_data = kSingleClientImpressionTestData; - impression_data.back().suppression_info = SuppressionInfo( - GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(3)); - - NotificationEntry entry(SchedulerClientType::kTest1, kGuid); - TestData test_data{impression_data, {entry}, SchedulerTaskTime::kMorning}; - ScheduleTask(test_data); -} - -// If two different types want to schedule at different times, pick the earilier -// one. -TEST_F(BackgroundTaskCoordinatorTest, ScheduleEarlierTime) { - const char kNow[] = "04/25/20 01:00:00 AM"; - SetNow(kNow); - EXPECT_CALL(*background_task(), Cancel()).Times(0); - // kTest1 type will run this evening, kTest2 will run task 3 days later. - // Expected to run the earilier task. - auto expected_window_start = GetTime("04/25/20 18:00:00 PM") - GetTime(kNow); - EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _)); - - NotificationEntry entry1(SchedulerClientType::kTest1, kGuid); - NotificationEntry entry2(SchedulerClientType::kTest2, "guid_entry2"); - auto impression_data = kClientsImpressionTestData; - impression_data[0].suppression_info = SuppressionInfo( - GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(3)); - TestData test_data{ - impression_data, {entry1, entry2}, SchedulerTaskTime::kMorning}; - ScheduleTask(test_data); -} - -// If reached |max_daily_shown_all_type|, background task should run tomorrow. -TEST_F(BackgroundTaskCoordinatorTest, InMorningThrottledAllTypes) { - const char kNow[] = "04/25/20 05:00:00 AM"; - SetNow(kNow); - EXPECT_CALL(*background_task(), Cancel()).Times(0); - // Expected to run task next morning. - auto expected_window_start = GetTime("04/26/20 06:00:00 AM") - GetTime(kNow); - EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _)); - - auto impression_data = kClientsImpressionTestData; - Impression impression; - impression.create_time = GetTime("04/25/20 01:00:00 AM"); - - // Make sure we reach daily max for all types. - for (int i = 0; i < config()->max_daily_shown_all_type; i++) - impression_data.back().impressions.emplace_back(impression); - - NotificationEntry entry(SchedulerClientType::kTest1, kGuid); - TestData test_data{impression_data, {entry}, SchedulerTaskTime::kMorning}; - ScheduleTask(test_data); -} - -// If reached |max_daily_shown_all_type| and all types have suppression, -// background task should run after one suppression expired. -TEST_F(BackgroundTaskCoordinatorTest, ThrottledAllTypesAndSuppression) { - const char kNow[] = "04/25/20 05:00:00 AM"; - SetNow(kNow); - EXPECT_CALL(*background_task(), Cancel()).Times(0); - // Expected to run after 3 days suppression ends. - auto expected_window_start = GetTime("04/28/20 06:00:00 AM") - GetTime(kNow); - EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _)); - - auto impression_data = kClientsImpressionTestData; - Impression impression; - impression.create_time = GetTime("04/25/20 01:00:00 AM"); - - // Make sure we reach daily max for all types. - for (int i = 0; i < config()->max_daily_shown_all_type; i++) - impression_data[1].impressions.emplace_back(impression); - - // Suppression for both types. - impression_data[0].suppression_info = SuppressionInfo( - GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(3)); - impression_data[1].suppression_info = SuppressionInfo( - GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(4)); - - NotificationEntry entry1(SchedulerClientType::kTest1, "test_guid_1"); - NotificationEntry entry2(SchedulerClientType::kTest2, "test_guid_2"); - TestData test_data{ - impression_data, {entry1, entry2}, SchedulerTaskTime::kMorning}; - ScheduleTask(test_data); -} - -} // namespace -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/collection_store.h b/chrome/browser/notifications/scheduler/collection_store.h deleted file mode 100644 index 25543c8..0000000 --- a/chrome/browser/notifications/scheduler/collection_store.h +++ /dev/null
@@ -1,53 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_COLLECTION_STORE_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_COLLECTION_STORE_H_ - -#include <memory> -#include <string> -#include <vector> - -#include "base/callback.h" -#include "base/macros.h" - -namespace notifications { - -// A storage interface which loads a collection of data type T into memory -// during initialization. When updating the data, T will be copied to the actual -// storage layer since the caller will keep in memory data as well. -template <typename T> -class CollectionStore { - public: - using Entries = std::vector<std::unique_ptr<T>>; - using LoadCallback = base::OnceCallback<void(bool, Entries)>; - using InitCallback = base::OnceCallback<void(bool)>; - using UpdateCallback = base::OnceCallback<void(bool)>; - - // Initializes the database and loads all entries into memory. - virtual void InitAndLoad(LoadCallback callback) = 0; - - // Adds an entry to the storage. - virtual void Add(const std::string& key, - const T& entry, - UpdateCallback callback) = 0; - - // Updates an entry. - virtual void Update(const std::string& key, - const T& entry, - UpdateCallback callback) = 0; - - // Deletes an entry from storage. - virtual void Delete(const std::string& key, UpdateCallback callback) = 0; - - CollectionStore() = default; - virtual ~CollectionStore() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(CollectionStore); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_COLLECTION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/display_decider.cc b/chrome/browser/notifications/scheduler/display_decider.cc deleted file mode 100644 index 91759454..0000000 --- a/chrome/browser/notifications/scheduler/display_decider.cc +++ /dev/null
@@ -1,190 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/display_decider.h" - -#include <algorithm> - -#include "chrome/browser/notifications/scheduler/distribution_policy.h" -#include "chrome/browser/notifications/scheduler/impression_types.h" -#include "chrome/browser/notifications/scheduler/notification_entry.h" -#include "chrome/browser/notifications/scheduler/scheduler_config.h" -#include "chrome/browser/notifications/scheduler/scheduler_utils.h" - -using Notifications = notifications::DisplayDecider::Notifications; -using Results = notifications::DisplayDecider::Results; -using ClientStates = notifications::DisplayDecider::ClientStates; - -namespace notifications { -namespace { - -// Helper class contains the actual logic to decide which notifications to show. -// This is an one-shot class, callers should create a new object each time. -class DecisionHelper { - public: - DecisionHelper(const SchedulerConfig* config, - const std::vector<SchedulerClientType>& clients, - std::unique_ptr<DistributionPolicy> distribution_policy, - SchedulerTaskTime task_start_time, - Notifications notifications, - ClientStates client_states) - : notifications_(std::move(notifications)), - current_task_start_time_(task_start_time), - client_states_(std::move(client_states)), - config_(config), - clients_(clients), - policy_(std::move(distribution_policy)), - daily_max_to_show_all_types_(0), - last_shown_type_(SchedulerClientType::kUnknown), - shown_(0) {} - - ~DecisionHelper() = default; - - // Figures out a list of notifications to show. - void DecideNotificationToShow(Results* results) { - ComputeDailyQuotaAllTypes(); - CountNotificationsShownToday(); - PickNotificationToShow(results); - } - - private: - void ComputeDailyQuotaAllTypes() { - // Only background task launch needs to comply to |policy_|. - if (current_task_start_time_ == SchedulerTaskTime::kUnknown) { - daily_max_to_show_all_types_ = config_->max_daily_shown_all_type; - return; - } - - int quota = std::max(config_->max_daily_shown_all_type - shown_, 0); - DCHECK(policy_); - daily_max_to_show_all_types_ = - std::min(config_->max_daily_shown_all_type, - policy_->MaxToShow(current_task_start_time_, quota)); - } - - void CountNotificationsShownToday() { - for (const auto& pair : client_states_) { - auto type = pair.first; - // TODO(xingliu): Ensure deprecated clients will not have data in storage. - DCHECK(std::find(clients_.begin(), clients_.end(), type) != - clients_.end()); - } - - NotificationsShownToday(client_states_, &shown_per_type_, &shown_, - &last_shown_type_); - } - - // Picks a list of notifications to show. - void PickNotificationToShow(Results* to_show) { - DCHECK(to_show); - if (shown_ > config_->max_daily_shown_all_type || clients_.empty()) - return; - - // No previous shown notification, move the iterator to last element. - // We will iterate through all client types later. - auto it = std::find(clients_.begin(), clients_.end(), last_shown_type_); - if (it == clients_.end()) { - DCHECK_EQ(last_shown_type_, SchedulerClientType::kUnknown); - last_shown_type_ = clients_.back(); - it = clients_.end() - 1; - } - - DCHECK_NE(last_shown_type_, SchedulerClientType::kUnknown); - size_t steps = 0u; - - // Circling around all clients to find new notification to show. - // TODO(xingliu): Apply scheduling parameters here. - do { - // Move the iterator to next client type. - DCHECK(it != clients_.end()); - if (++it == clients_.end()) - it = clients_.begin(); - ++steps; - - SchedulerClientType type = *it; - - // Check quota for all types and current background task type. - if (ReachDailyQuota()) - break; - - // Check quota for this type, and continue to iterate other types. - if (NoMoreNotificationToShow(type)) - continue; - - // Show the last notification in the vector. Notice the order depends on - // how the vector is sorted. - to_show->emplace(notifications_[type].back()->guid); - notifications_[type].pop_back(); - shown_per_type_[type]++; - shown_++; - steps = 0u; - - // Stop if we didn't find anything new to show, and have looped around - // all clients. - } while (steps <= clients_.size()); - } - - bool NoMoreNotificationToShow(SchedulerClientType type) { - auto it = client_states_.find(type); - int max_daily_show = - it == client_states_.end() ? 0 : it->second->current_max_daily_show; - - return notifications_[type].empty() || - shown_per_type_[type] >= config_->max_daily_shown_per_type || - shown_per_type_[type] >= max_daily_show; - } - - bool ReachDailyQuota() const { - return shown_ >= daily_max_to_show_all_types_; - } - - // Scheduled notifications as candidates to display to the user. - Notifications notifications_; - - const SchedulerTaskTime current_task_start_time_; - const ClientStates client_states_; - const SchedulerConfig* config_; - const std::vector<SchedulerClientType> clients_; - std::unique_ptr<DistributionPolicy> policy_; - int daily_max_to_show_all_types_; - - SchedulerClientType last_shown_type_; - std::map<SchedulerClientType, int> shown_per_type_; - int shown_; - - DISALLOW_COPY_AND_ASSIGN(DecisionHelper); -}; - -class DisplayDeciderImpl : public DisplayDecider { - public: - DisplayDeciderImpl() = default; - ~DisplayDeciderImpl() override = default; - - private: - // DisplayDecider implementation. - void FindNotificationsToShow( - const SchedulerConfig* config, - std::vector<SchedulerClientType> clients, - std::unique_ptr<DistributionPolicy> distribution_policy, - SchedulerTaskTime task_start_time, - Notifications notifications, - ClientStates client_states, - Results* results) override { - auto helper = std::make_unique<DecisionHelper>( - config, std::move(clients), std::move(distribution_policy), - task_start_time, std::move(notifications), std::move(client_states)); - helper->DecideNotificationToShow(results); - } - - DISALLOW_COPY_AND_ASSIGN(DisplayDeciderImpl); -}; - -} // namespace - -// static -std::unique_ptr<DisplayDecider> DisplayDecider::Create() { - return std::make_unique<DisplayDeciderImpl>(); -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/display_decider.h b/chrome/browser/notifications/scheduler/display_decider.h deleted file mode 100644 index 2e4187c6..0000000 --- a/chrome/browser/notifications/scheduler/display_decider.h +++ /dev/null
@@ -1,58 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_DISPLAY_DECIDER_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_DISPLAY_DECIDER_H_ - -#include <map> -#include <memory> -#include <set> -#include <utility> -#include <vector> - -#include "base/callback.h" -#include "base/macros.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" - -namespace notifications { - -class DistributionPolicy; -struct ClientState; -struct NotificationEntry; -struct SchedulerConfig; - -// This class uses scheduled notifications data and notification impression data -// of each notification type to find a list of notification that should be -// displayed to the user. -// All operations should be done on the main thread. -class DisplayDecider { - public: - using Notifications = - std::map<SchedulerClientType, std::vector<const NotificationEntry*>>; - using ClientStates = std::map<SchedulerClientType, const ClientState*>; - using Results = std::set<std::string>; - - // Creates the decider to determine notifications to show. - static std::unique_ptr<DisplayDecider> Create(); - - DisplayDecider() = default; - virtual ~DisplayDecider() = default; - - // Finds notifications to show. Returns a list of notification guids. - virtual void FindNotificationsToShow( - const SchedulerConfig* config, - std::vector<SchedulerClientType> clients, - std::unique_ptr<DistributionPolicy> distribution_policy, - SchedulerTaskTime task_start_time, - Notifications notifications, - ClientStates client_states, - Results* results) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(DisplayDecider); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_DISPLAY_DECIDER_H_
diff --git a/chrome/browser/notifications/scheduler/display_decider_unittest.cc b/chrome/browser/notifications/scheduler/display_decider_unittest.cc deleted file mode 100644 index 8d606339..0000000 --- a/chrome/browser/notifications/scheduler/display_decider_unittest.cc +++ /dev/null
@@ -1,151 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/display_decider.h" - -#include <map> -#include <memory> -#include <vector> - -#include "base/strings/stringprintf.h" -#include "base/test/scoped_task_environment.h" -#include "chrome/browser/notifications/scheduler/distribution_policy.h" -#include "chrome/browser/notifications/scheduler/notification_entry.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" -#include "chrome/browser/notifications/scheduler/scheduler_config.h" -#include "chrome/browser/notifications/scheduler/test/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace notifications { -namespace { - -// Default suppression info used in this test. -const SuppressionInfo kSuppressionInfo = - SuppressionInfo(base::Time::Now(), base::TimeDelta::FromDays(56)); - -// Initial state for test cases with a single registered client. -const std::vector<test::ImpressionTestData> kSingleClientImpressionTestData = { - {SchedulerClientType::kTest1, - 2 /* current_max_daily_show */, - {}, - base::nullopt /* suppression_info */}}; - -const std::vector<test::ImpressionTestData> kClientsImpressionTestData = { - {SchedulerClientType::kTest1, - 2 /* current_max_daily_show */, - {}, - base::nullopt /* suppression_info */}, - {SchedulerClientType::kTest2, - 5 /* current_max_daily_show */, - {}, - base::nullopt /* suppression_info */}, - {SchedulerClientType::kTest3, - 0 /* current_max_daily_show */, - {}, - kSuppressionInfo}}; - -struct TestData { - // Impression data as the input. - std::vector<test::ImpressionTestData> impression_test_data; - - // Notification entries as the input. - std::vector<NotificationEntry> notification_entries; - - // The type of current background task. - SchedulerTaskTime task_start_time = SchedulerTaskTime::kUnknown; - - // Expected output data. - DisplayDecider::Results expected; -}; - -std::string DebugString(const DisplayDecider::Results& results) { - std::string debug_string("notifications_to_show: \n"); - for (const auto& guid : results) - debug_string += base::StringPrintf("%s ", guid.c_str()); - - return debug_string; -} - -// TODO(xingliu): Add more test cases. -class DisplayDeciderTest : public testing::Test { - public: - DisplayDeciderTest() = default; - ~DisplayDeciderTest() override = default; - - void SetUp() override { - // Setup configuration used by this test. - config_.morning_task_hour = 7; - config_.evening_task_hour = 18; - config_.max_daily_shown_all_type = 3; - } - - protected: - // Initializes a test case with input data. - void RunTestCase(const TestData& test_data) { - test_data_ = test_data; - test::AddImpressionTestData(test_data_.impression_test_data, - &client_states_); - - DisplayDecider::Notifications notifications; - for (const auto& entry : test_data_.notification_entries) { - notifications[entry.type].emplace_back(&entry); - } - std::vector<SchedulerClientType> clients; - - std::map<SchedulerClientType, const ClientState*> client_states; - for (const auto& type : client_states_) { - client_states.emplace(type.first, type.second.get()); - clients.emplace_back(type.first); - } - - // Copy test inputs into |decider_|. - decider_ = DisplayDecider::Create(); - decider_->FindNotificationsToShow( - &config_, std::move(clients), DistributionPolicy::Create(), - test_data_.task_start_time, std::move(notifications), - std::move(client_states), &results_); - - // Verify output. - EXPECT_EQ(results_, test_data_.expected) - << "Actual result: \n" - << DebugString(results_) << " \n Expected result: \n" - << DebugString(test_data_.expected); - } - - private: - base::test::ScopedTaskEnvironment scoped_task_environment_; - - TestData test_data_; - SchedulerConfig config_; - - std::map<SchedulerClientType, std::unique_ptr<ClientState>> client_states_; - - // Test target class and output. - std::unique_ptr<DisplayDecider> decider_; - DisplayDecider::Results results_; - - DISALLOW_COPY_AND_ASSIGN(DisplayDeciderTest); -}; - -TEST_F(DisplayDeciderTest, NoNotification) { - TestData data{kClientsImpressionTestData, - {}, - SchedulerTaskTime::kEvening, - DisplayDecider::Results()}; - RunTestCase(data); -} - -// Simple test case to verify new notifcaiton can be selected to show. -TEST_F(DisplayDeciderTest, PickNewMorning) { - NotificationEntry entry(SchedulerClientType::kTest1, "guid123"); - DisplayDecider::Results expected = {"guid123"}; - TestData data{kSingleClientImpressionTestData, - {entry}, - SchedulerTaskTime::kMorning, - std::move(expected)}; - RunTestCase(data); -} - -} // namespace -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/distribution_policy.cc b/chrome/browser/notifications/scheduler/distribution_policy.cc deleted file mode 100644 index b30cbfc..0000000 --- a/chrome/browser/notifications/scheduler/distribution_policy.cc +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/distribution_policy.h" - -#include "base/logging.h" - -namespace notifications { -namespace { - -// Evenly distributes notifications to show in morning and evening. Morning -// will have one more to show if the total quota is odd. -class EvenDistributionHigherMorning : public DistributionPolicy { - public: - EvenDistributionHigherMorning() = default; - ~EvenDistributionHigherMorning() override = default; - - private: - // DistributionPolicy implementation. - int MaxToShow(SchedulerTaskTime task_start_time, int quota) override { - DCHECK_GE(quota, 0); - switch (task_start_time) { - case SchedulerTaskTime::kUnknown: - NOTREACHED(); - return quota; - case SchedulerTaskTime::kMorning: - return quota / 2 + quota % 2; - case SchedulerTaskTime::kEvening: - // The task running in the evening should flush all the remaining - // notifications. - return quota; - } - } - - DISALLOW_COPY_AND_ASSIGN(EvenDistributionHigherMorning); -}; - -} // namespace - -// static -std::unique_ptr<DistributionPolicy> DistributionPolicy::Create() { - return std::make_unique<EvenDistributionHigherMorning>(); -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/distribution_policy.h b/chrome/browser/notifications/scheduler/distribution_policy.h deleted file mode 100644 index 762ba338..0000000 --- a/chrome/browser/notifications/scheduler/distribution_policy.h +++ /dev/null
@@ -1,36 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_DISTRIBUTION_POLICY_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_DISTRIBUTION_POLICY_H_ - -#include <memory> - -#include "base/macros.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" - -namespace notifications { - -// Defines how to distribute notifications to show in different background tasks -// in a day. -class DistributionPolicy { - public: - // Creates the default distribution policy. - static std::unique_ptr<DistributionPolicy> Create(); - - DistributionPolicy() = default; - virtual ~DistributionPolicy() = default; - - // Returns the maximum number of notifications to show in the background task - // starts at |task_start_time|. Suppose we at most can show |quota| number of - // new notifications during the current background task. - virtual int MaxToShow(SchedulerTaskTime task_start_time, int quota) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(DistributionPolicy); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_DISTRIBUTION_POLICY_H_
diff --git a/chrome/browser/notifications/scheduler/distribution_policy_unittest.cc b/chrome/browser/notifications/scheduler/distribution_policy_unittest.cc deleted file mode 100644 index 320b900..0000000 --- a/chrome/browser/notifications/scheduler/distribution_policy_unittest.cc +++ /dev/null
@@ -1,42 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/distribution_policy.h" - -#include "base/logging.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace notifications { -namespace { - -int MaxToShow(DistributionPolicy* policy, - SchedulerTaskTime task_start_time, - int quota) { - DCHECK(policy); - return policy->MaxToShow(task_start_time, quota); -} - -TEST(DistributionPolicyTest, EvenDistributionHigherMorning) { - auto policy = DistributionPolicy::Create(); - - EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 5 /* quota */), - 3); - EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 4 /* quota */), - 2); - - // Evening task should flush all remaining quota. - EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kEvening, 5 /* quota */), - 5); - EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kEvening, 4 /* quota */), - 4); - - // Test 0 quota. - EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 0 /* quota */), - 0); - EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 0 /* quota */), - 0); -} - -} // namespace -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/icon_entry.cc b/chrome/browser/notifications/scheduler/icon_entry.cc deleted file mode 100644 index 01f87e0..0000000 --- a/chrome/browser/notifications/scheduler/icon_entry.cc +++ /dev/null
@@ -1,16 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/icon_entry.h" - -namespace notifications { - -IconEntry::IconEntry() = default; - -IconEntry::IconEntry(IconEntry&& other) { - uuid.swap(other.uuid); - data.swap(other.data); -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/icon_entry.h b/chrome/browser/notifications/scheduler/icon_entry.h deleted file mode 100644 index 039c295..0000000 --- a/chrome/browser/notifications/scheduler/icon_entry.h +++ /dev/null
@@ -1,38 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_ICON_ENTRY_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_ICON_ENTRY_H_ - -#include <string> -#include <utility> - -#include "base/macros.h" - -namespace notifications { - -// The database entry that contains a notification icon, deserialized from the -// icon protobuffer. -// The icon can be a large chunk of memory so should be used in caution. The -// format of the data is the same as the format in the protobuffer, and may need -// to be converted to bitmap when used by the UI. -struct IconEntry { - using IconData = std::string; - - IconEntry(); - IconEntry(IconEntry&& other); - - // Unique identifier for the icon database entry. - std::string uuid; - - // Raw data of the icon. - IconData data; - - private: - DISALLOW_COPY_AND_ASSIGN(IconEntry); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_ICON_ENTRY_H_
diff --git a/chrome/browser/notifications/scheduler/icon_store.cc b/chrome/browser/notifications/scheduler/icon_store.cc deleted file mode 100644 index a228d38..0000000 --- a/chrome/browser/notifications/scheduler/icon_store.cc +++ /dev/null
@@ -1,89 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/icon_store.h" - -#include <utility> - -#include "chrome/browser/notifications/scheduler/icon_entry.h" -#include "chrome/browser/notifications/scheduler/proto_conversion.h" - -namespace leveldb_proto { - -void DataToProto(notifications::IconEntry* icon_entry, - notifications::proto::Icon* proto) { - IconEntryToProto(icon_entry, proto); -} - -void ProtoToData(notifications::proto::Icon* proto, - notifications::IconEntry* icon_entry) { - IconEntryFromProto(proto, icon_entry); -} - -} // namespace leveldb_proto - -namespace notifications { - -using KeyEntryPair = std::pair<std::string, IconEntry>; -using KeyEntryVector = std::vector<KeyEntryPair>; -using KeyVector = std::vector<std::string>; - -IconProtoDbStore::IconProtoDbStore( - std::unique_ptr<leveldb_proto::ProtoDatabase<proto::Icon, IconEntry>> db) - : db_(std::move(db)), weak_ptr_factory_(this) {} - -IconProtoDbStore::~IconProtoDbStore() = default; - -void IconProtoDbStore::Init(InitCallback callback) { - db_->Init(base::BindOnce(&IconProtoDbStore::OnDbInitialized, - weak_ptr_factory_.GetWeakPtr(), - std::move(callback))); -} - -void IconProtoDbStore::Load(const std::string& key, LoadCallback callback) { - db_->GetEntry( - key, base::BindOnce(&IconProtoDbStore::OnIconEntryLoaded, - weak_ptr_factory_.GetWeakPtr(), std::move(callback))); -} - -void IconProtoDbStore::Add(const std::string& key, - std::unique_ptr<IconEntry> entry, - UpdateCallback callback) { - auto entries_to_save = std::make_unique<KeyEntryVector>(); - // TODO(xingliu): See if proto database can use - // std::vector<std::unique_ptr<T>> to avoid copy T into std::pair. Currently - // some code uses the copy constructor that force T to be copyable. - entries_to_save->emplace_back( - std::make_pair(key, std::move(*entry.release()))); - db_->UpdateEntries(std::move(entries_to_save), std::make_unique<KeyVector>(), - std::move(callback)); -} - -void IconProtoDbStore::Delete(const std::string& key, UpdateCallback callback) { - auto keys_to_delete = std::make_unique<KeyVector>(); - keys_to_delete->emplace_back(key); - db_->UpdateEntries(std::make_unique<KeyEntryVector>(), - std::move(keys_to_delete), std::move(callback)); -} - -void IconProtoDbStore::OnDbInitialized( - InitCallback callback, - leveldb_proto::Enums::InitStatus status) { - bool success = (status == leveldb_proto::Enums::InitStatus::kOK); - std::move(callback).Run(success); -} - -void IconProtoDbStore::OnIconEntryLoaded( - LoadCallback callback, - bool success, - std::unique_ptr<IconEntry> icon_entry) { - if (!success) { - std::move(callback).Run(false, nullptr); - return; - } - - std::move(callback).Run(true, std::move(icon_entry)); -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/icon_store.h b/chrome/browser/notifications/scheduler/icon_store.h deleted file mode 100644 index 2fece4e..0000000 --- a/chrome/browser/notifications/scheduler/icon_store.h +++ /dev/null
@@ -1,95 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_ICON_STORE_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_ICON_STORE_H_ - -#include <memory> -#include <string> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "chrome/browser/notifications/proto/icon.pb.h" -#include "chrome/browser/notifications/scheduler/icon_entry.h" -#include "components/leveldb_proto/public/proto_database.h" - -// Forward declaration for proto conversion. -namespace leveldb_proto { -void DataToProto(notifications::IconEntry* icon_entry, - notifications::proto::Icon* proto); - -void ProtoToData(notifications::proto::Icon* proto, - notifications::IconEntry* icon_entry); -} // namespace leveldb_proto - -namespace notifications { - -// Storage interface used to read/write icon data, each time only one icon can -// be loaded into memory. -class IconStore { - public: - using LoadCallback = - base::OnceCallback<void(bool, std::unique_ptr<IconEntry>)>; - using InitCallback = base::OnceCallback<void(bool)>; - using UpdateCallback = base::OnceCallback<void(bool)>; - - // Initializes the storage. - virtual void Init(InitCallback callback) = 0; - - // Loads one icon. - virtual void Load(const std::string& key, LoadCallback callback) = 0; - - // Adds one icon to storage. - virtual void Add(const std::string& key, - std::unique_ptr<IconEntry> entry, - UpdateCallback callback) = 0; - - // Deletes an icon. - virtual void Delete(const std::string& key, UpdateCallback callback) = 0; - - IconStore() = default; - virtual ~IconStore() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(IconStore); -}; - -// IconStore implementation backed by a proto database. -class IconProtoDbStore : public IconStore { - public: - explicit IconProtoDbStore( - std::unique_ptr<leveldb_proto::ProtoDatabase<proto::Icon, IconEntry>> db); - ~IconProtoDbStore() override; - - private: - // IconStore implementation. - void Init(InitCallback callback) override; - void Load(const std::string& key, LoadCallback callback) override; - void Add(const std::string& key, - std::unique_ptr<IconEntry> entry, - UpdateCallback callback) override; - void Delete(const std::string& key, UpdateCallback callback) override; - - // Called when the proto database is initialized. - void OnDbInitialized(InitCallback callback, - leveldb_proto::Enums::InitStatus status); - - // Called when the icon is retrieved from the database. - void OnIconEntryLoaded(LoadCallback callback, - bool success, - std::unique_ptr<IconEntry> icon_entry); - - // The proto database instance that persists data. - std::unique_ptr<leveldb_proto::ProtoDatabase<proto::Icon, IconEntry>> db_; - - base::WeakPtrFactory<IconProtoDbStore> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(IconProtoDbStore); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_ICON_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/icon_store_unittest.cc b/chrome/browser/notifications/scheduler/icon_store_unittest.cc deleted file mode 100644 index c15c4c8..0000000 --- a/chrome/browser/notifications/scheduler/icon_store_unittest.cc +++ /dev/null
@@ -1,170 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/icon_store.h" - -#include <map> -#include <memory> -#include <string> -#include <utility> - -#include "base/run_loop.h" -#include "base/test/scoped_task_environment.h" -#include "chrome/browser/notifications/proto/icon.pb.h" -#include "chrome/browser/notifications/scheduler/icon_entry.h" -#include "components/leveldb_proto/testing/fake_db.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace notifications { -namespace { - -const char kEntryKey[] = "guid_key1"; -const char kEntryKey2[] = "guid_key2"; -const char kEntryId[] = "proto_id_1"; -const char kEntryId2[] = "proto_id_2"; -const char kEntryData[] = "data_1"; -const char kEntryData2[] = "data_2"; - -class IconStoreTest : public testing::Test { - public: - IconStoreTest() : load_result_(false), db_(nullptr) {} - ~IconStoreTest() override = default; - - void SetUp() override { - IconEntry entry; - entry.uuid = kEntryId; - entry.data = kEntryData; - proto::Icon proto; - leveldb_proto::DataToProto(&entry, &proto); - db_entries_.emplace(kEntryKey, proto); - - auto db = - std::make_unique<leveldb_proto::test::FakeDB<proto::Icon, IconEntry>>( - &db_entries_); - db_ = db.get(); - store_ = std::make_unique<IconProtoDbStore>(std::move(db)); - } - - void InitDb() { - store()->Init(base::DoNothing()); - db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK); - } - - void Load(const std::string& key) { - store()->Load(key, base::BindOnce(&IconStoreTest::OnEntryLoaded, - base::Unretained(this))); - } - - void OnEntryLoaded(bool success, std::unique_ptr<IconEntry> entry) { - loaded_entry_ = std::move(entry); - load_result_ = success; - } - - protected: - IconStore* store() { return store_.get(); } - leveldb_proto::test::FakeDB<proto::Icon, IconEntry>* db() { return db_; } - bool load_result() const { return load_result_; } - IconEntry* loaded_entry() { return loaded_entry_.get(); } - - void VerifyEntry(const std::string& uuid, const std::string& data) { - DCHECK(loaded_entry_); - EXPECT_EQ(loaded_entry_->uuid, uuid); - EXPECT_EQ(loaded_entry_->data, data); - } - - private: - base::test::ScopedTaskEnvironment scoped_task_environment_; - std::unique_ptr<IconStore> store_; - std::map<std::string, proto::Icon> db_entries_; - std::unique_ptr<IconEntry> loaded_entry_; - bool load_result_; - leveldb_proto::test::FakeDB<proto::Icon, IconEntry>* db_; - - DISALLOW_COPY_AND_ASSIGN(IconStoreTest); -}; - -TEST_F(IconStoreTest, Init) { - store()->Init(base::BindOnce([](bool success) { EXPECT_TRUE(success); })); - db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK); - base::RunLoop().RunUntilIdle(); -} - -TEST_F(IconStoreTest, InitFailed) { - store()->Init(base::BindOnce([](bool success) { EXPECT_FALSE(success); })); - db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kCorrupt); - base::RunLoop().RunUntilIdle(); -} - -TEST_F(IconStoreTest, LoadOne) { - InitDb(); - Load(kEntryKey); - db()->GetCallback(true); - - // Verify data is loaded. - DCHECK(loaded_entry()); - EXPECT_TRUE(load_result()); - VerifyEntry(kEntryId, kEntryData); -} - -TEST_F(IconStoreTest, LoadFailed) { - InitDb(); - Load(kEntryKey); - db()->GetCallback(false); - - // Verify load failure. - EXPECT_FALSE(loaded_entry()); - EXPECT_FALSE(load_result()); -} - -TEST_F(IconStoreTest, Add) { - InitDb(); - - auto new_entry = std::make_unique<IconEntry>(); - new_entry->uuid = kEntryId2; - new_entry->data = kEntryData; - - store()->Add(kEntryKey2, std::move(new_entry), base::DoNothing()); - db()->UpdateCallback(true); - - // Verify the entry is added. - Load(kEntryKey2); - db()->GetCallback(true); - EXPECT_TRUE(load_result()); - VerifyEntry(kEntryId2, kEntryData); -} - -TEST_F(IconStoreTest, AddDuplicate) { - InitDb(); - - auto new_entry = std::make_unique<IconEntry>(); - new_entry->uuid = kEntryId; - new_entry->data = kEntryData2; - - store()->Add(kEntryKey, std::move(new_entry), base::DoNothing()); - db()->UpdateCallback(true); - - // Add a duplicate id is currently allowed, we just update the entry. - Load(kEntryKey); - db()->GetCallback(true); - EXPECT_TRUE(load_result()); - VerifyEntry(kEntryId, kEntryData2); -} - -TEST_F(IconStoreTest, Delete) { - InitDb(); - - // Delete the only entry. - store()->Delete(kEntryKey, - base::BindOnce([](bool success) { EXPECT_TRUE(success); })); - db()->UpdateCallback(true); - - // No entry can be loaded, move nullptr as result. - Load(kEntryKey); - db()->GetCallback(true); - EXPECT_FALSE(loaded_entry()); - EXPECT_TRUE(load_result()); -} - -} // namespace -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_history_tracker.cc b/chrome/browser/notifications/scheduler/impression_history_tracker.cc deleted file mode 100644 index def4dc4..0000000 --- a/chrome/browser/notifications/scheduler/impression_history_tracker.cc +++ /dev/null
@@ -1,392 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/impression_history_tracker.h" - -#include <algorithm> -#include <utility> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/numerics/ranges.h" - -namespace notifications { - -// Comparator used to sort notification entries based on creation time. -bool CreateTimeCompare(const Impression& lhs, const Impression& rhs) { - return lhs.create_time < rhs.create_time; -} - -std::string ToDatabaseKey(SchedulerClientType type) { - switch (type) { - case SchedulerClientType::kTest1: - return "Test1"; - case SchedulerClientType::kTest2: - return "Test2"; - case SchedulerClientType::kTest3: - return "Test3"; - case SchedulerClientType::kUnknown: - NOTREACHED(); - return std::string(); - } -} - -ImpressionHistoryTrackerImpl::ImpressionHistoryTrackerImpl( - const SchedulerConfig& config, - std::unique_ptr<CollectionStore<ClientState>> store) - : store_(std::move(store)), - config_(config), - initialized_(false), - delegate_(nullptr), - weak_ptr_factory_(this) {} - -ImpressionHistoryTrackerImpl::~ImpressionHistoryTrackerImpl() = default; - -void ImpressionHistoryTrackerImpl::Init(Delegate* delegate, - InitCallback callback) { - DCHECK(!delegate_ && delegate); - delegate_ = delegate; - store_->InitAndLoad( - base::BindOnce(&ImpressionHistoryTrackerImpl::OnStoreInitialized, - weak_ptr_factory_.GetWeakPtr(), std::move(callback))); -} - -void ImpressionHistoryTrackerImpl::AnalyzeImpressionHistory() { - for (auto& client_state : client_states_) - AnalyzeImpressionHistory(client_state.second.get()); - if (MaybeUpdateAllDb()) - NotifyImpressionUpdate(); -} - -void ImpressionHistoryTrackerImpl::GetClientStates( - std::map<SchedulerClientType, const ClientState*>* client_states) const { - DCHECK(client_states); - client_states->clear(); - for (const auto& pair : client_states_) { - client_states->emplace(pair.first, pair.second.get()); - } -} - -void ImpressionHistoryTrackerImpl::OnClick(const std::string& notification_id) { - OnClickInternal(notification_id, true /*update_db*/); -} - -void ImpressionHistoryTrackerImpl::OnActionClick( - const std::string& notification_id, - ActionButtonType button_type) { - OnButtonClickInternal(notification_id, button_type, true /*update_db*/); -} - -void ImpressionHistoryTrackerImpl::OnDismiss( - const std::string& notification_id) { - OnDismissInternal(notification_id, true /*update_db*/); -} - -void ImpressionHistoryTrackerImpl::OnStoreInitialized( - InitCallback callback, - bool success, - CollectionStore<ClientState>::Entries entries) { - if (!success) { - std::move(callback).Run(false); - return; - } - - initialized_ = true; - - // Load the data to memory, and sort the impression list. - // TODO(xingliu): Persist ClientState for new registered client and remove - // deprecated client. https://crbug.com/968606. - for (auto it = entries.begin(); it != entries.end(); ++it) { - auto& entry = (*it); - auto type = entry->type; - std::sort(entry->impressions.begin(), entry->impressions.end(), - &CreateTimeCompare); - for (auto& impression : entry->impressions) { - impression_map_.emplace(impression.guid, &impression); - } - client_states_.emplace(type, std::move(*it)); - } - - std::move(callback).Run(true); -} - -void ImpressionHistoryTrackerImpl::AnalyzeImpressionHistory( - ClientState* client_state) { - DCHECK(client_state); - std::deque<Impression*> dismisses; - base::Time now = base::Time::Now(); - - for (auto it = client_state->impressions.begin(); - it != client_state->impressions.end();) { - auto* impression = &*it; - - // Prune out expired impression. - if (now - impression->create_time > config_.impression_expiration) { - impression_map_.erase(impression->guid); - client_state->impressions.erase(it++); - SetNeedsUpdate(client_state->type, true); - continue; - } else { - ++it; - } - - // TODO(xingliu): Use scheduling params to determine ImpressionResult. - // https://crbug.com/968880 - switch (impression->feedback) { - case UserFeedback::kDismiss: - dismisses.emplace_back(impression); - PruneImpressionByCreateTime( - &dismisses, impression->create_time - config_.dismiss_duration); - - // Three consecutive dismisses will result in suppression. - ApplyNegativeImpressions(client_state, &dismisses, - config_.dismiss_count); - break; - case UserFeedback::kClick: - OnClickInternal(impression->guid, false /*update_db*/); - break; - case UserFeedback::kHelpful: - OnButtonClickInternal(impression->guid, ActionButtonType::kHelpful, - false /*update_db*/); - break; - case UserFeedback::kNotHelpful: - OnButtonClickInternal(impression->guid, ActionButtonType::kUnhelpful, - false /*update_db*/); - break; - case UserFeedback::kIgnore: - break; - case UserFeedback::kNoFeedback: - FALLTHROUGH; - default: - // The user didn't interact with the notification yet. - continue; - break; - } - } - - // Check suppression expiration. - CheckSuppressionExpiration(client_state); -} - -// static -void ImpressionHistoryTrackerImpl::PruneImpressionByCreateTime( - std::deque<Impression*>* impressions, - const base::Time& start_time) { - DCHECK(impressions); - while (!impressions->empty()) { - if (impressions->front()->create_time > start_time) - break; - // Anything created before |start_time| is considered to have no effect and - // will never be processed again. - impressions->front()->integrated = true; - impressions->pop_front(); - } -} - -void ImpressionHistoryTrackerImpl::ApplyPositiveImpression( - ClientState* client_state, - Impression* impression) { - DCHECK(impression); - if (impression->integrated) - return; - - SetNeedsUpdate(client_state->type, true); - impression->integrated = true; - impression->impression = ImpressionResult::kPositive; - - // A positive impression directly releases the suppression. - if (client_state->suppression_info.has_value()) { - client_state->current_max_daily_show = - client_state->suppression_info->recover_goal; - client_state->suppression_info.reset(); - return; - } - - // Increase |current_max_daily_show| by 1. - client_state->current_max_daily_show = - base::ClampToRange(++client_state->current_max_daily_show, 0, - config_.max_daily_shown_per_type); -} - -void ImpressionHistoryTrackerImpl::ApplyNegativeImpressions( - ClientState* client_state, - std::deque<Impression*>* impressions, - size_t num_actions) { - if (impressions->size() < num_actions) - return; - - // Suppress the notification if the user performed consecutive operations that - // generates negative impressions. - for (size_t i = 0, size = impressions->size(); i < size; ++i) { - if ((*impressions)[i]->integrated) - continue; - - (*impressions)[i]->integrated = true; - - // Each user feedback after |num_action| will apply a new negative - // impression. - if (i + 1 >= num_actions) - ApplyNegativeImpression(client_state, (*impressions)[i]); - } -} - -void ImpressionHistoryTrackerImpl::ApplyNegativeImpression( - ClientState* client_state, - Impression* impression) { - if (impression->integrated) - return; - - SetNeedsUpdate(client_state->type, true); - impression->integrated = true; - impression->impression = ImpressionResult::kNegative; - - // Suppress the notification, the user will not see this type of notification - // for a while. - SuppressionInfo supression_info(base::Time::Now(), - config_.suppression_duration); - client_state->suppression_info = std::move(supression_info); - client_state->current_max_daily_show = 0; -} - -void ImpressionHistoryTrackerImpl::CheckSuppressionExpiration( - ClientState* client_state) { - // No suppression to recover from. - if (!client_state->suppression_info.has_value()) - return; - - SuppressionInfo& suppression = client_state->suppression_info.value(); - base::Time now = base::Time::Now(); - - // Still in the suppression time window. - if (now - suppression.last_trigger_time < suppression.duration) - return; - - // Recover from suppression and increase |current_max_daily_show|. - DCHECK_EQ(client_state->current_max_daily_show, 0); - client_state->current_max_daily_show = suppression.recover_goal; - - // Clear suppression if fully recovered. - client_state->suppression_info.reset(); - SetNeedsUpdate(client_state->type, true); -} - -bool ImpressionHistoryTrackerImpl::MaybeUpdateDb(SchedulerClientType type) { - auto it = client_states_.find(type); - if (it == client_states_.end()) - return false; - - bool db_updated = false; - if (NeedsUpdate(type)) { - store_->Update(ToDatabaseKey(type), *(it->second.get()), base::DoNothing()); - db_updated = true; - } - SetNeedsUpdate(type, false); - return db_updated; -} - -bool ImpressionHistoryTrackerImpl::MaybeUpdateAllDb() { - bool db_updated = false; - for (const auto& client_state : client_states_) { - auto type = client_state.second->type; - db_updated |= MaybeUpdateDb(type); - } - - return db_updated; -} - -void ImpressionHistoryTrackerImpl::SetNeedsUpdate(SchedulerClientType type, - bool needs_update) { - need_update_db_[type] = needs_update; -} - -bool ImpressionHistoryTrackerImpl::NeedsUpdate(SchedulerClientType type) const { - auto it = need_update_db_.find(type); - return it == need_update_db_.end() ? false : it->second; -} - -void ImpressionHistoryTrackerImpl::NotifyImpressionUpdate() { - if (delegate_) - delegate_->OnImpressionUpdated(); -} - -Impression* ImpressionHistoryTrackerImpl::FindImpressionNeedsUpdate( - const std::string& notification_guid) { - auto it = impression_map_.find(notification_guid); - if (it == impression_map_.end()) - return nullptr; - auto* impression = it->second; - - if (impression->integrated) - return nullptr; - - return it->second; -} - -void ImpressionHistoryTrackerImpl::OnClickInternal( - const std::string& notification_guid, - bool update_db) { - auto* impression = FindImpressionNeedsUpdate(notification_guid); - if (!impression) - return; - - auto it = client_states_.find(impression->type); - if (it == client_states_.end()) - return; - ClientState* client_state = it->second.get(); - ApplyPositiveImpression(client_state, impression); - impression->feedback = UserFeedback::kClick; - - if (update_db && MaybeUpdateDb(client_state->type)) - NotifyImpressionUpdate(); -} - -void ImpressionHistoryTrackerImpl::OnButtonClickInternal( - const std::string& notification_guid, - ActionButtonType button_type, - bool update_db) { - auto* impression = FindImpressionNeedsUpdate(notification_guid); - if (!impression) - return; - auto it = client_states_.find(impression->type); - if (it == client_states_.end()) - return; - - ClientState* client_state = it->second.get(); - switch (button_type) { - case ActionButtonType::kHelpful: - ApplyPositiveImpression(client_state, impression); - impression->feedback = UserFeedback::kHelpful; - break; - case ActionButtonType::kUnhelpful: - ApplyNegativeImpression(client_state, impression); - impression->feedback = UserFeedback::kNotHelpful; - break; - case ActionButtonType::kUnknownAction: - NOTIMPLEMENTED(); - break; - } - - if (update_db && MaybeUpdateDb(client_state->type)) - NotifyImpressionUpdate(); -} - -void ImpressionHistoryTrackerImpl::OnDismissInternal( - const std::string& notification_guid, - bool update_db) { - auto* impression = FindImpressionNeedsUpdate(notification_guid); - if (!impression) - return; - - auto it = client_states_.find(impression->type); - if (it == client_states_.end()) - return; - ClientState* client_state = it->second.get(); - - AnalyzeImpressionHistory(client_state); - - if (update_db && MaybeUpdateDb(client_state->type)) - NotifyImpressionUpdate(); -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_history_tracker.h b/chrome/browser/notifications/scheduler/impression_history_tracker.h deleted file mode 100644 index e409fce..0000000 --- a/chrome/browser/notifications/scheduler/impression_history_tracker.h +++ /dev/null
@@ -1,163 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_IMPRESSION_HISTORY_TRACKER_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_HISTORY_TRACKER_H_ - -#include <deque> -#include <map> -#include <memory> -#include <string> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/time/time.h" -#include "chrome/browser/notifications/scheduler/collection_store.h" -#include "chrome/browser/notifications/scheduler/impression_types.h" -#include "chrome/browser/notifications/scheduler/scheduler_config.h" -#include "chrome/browser/notifications/scheduler/user_action_handler.h" - -namespace notifications { - -// Provides functionalities to update notification impression history and adjust -// maximum daily notification shown to the user. -class ImpressionHistoryTracker : public UserActionHandler { - public: - using ClientStates = - std::map<SchedulerClientType, std::unique_ptr<ClientState>>; - using InitCallback = base::OnceCallback<void(bool)>; - - class Delegate { - public: - Delegate() = default; - virtual ~Delegate() = default; - - // Called when the impression data is updated. - virtual void OnImpressionUpdated() = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(Delegate); - }; - - // Initializes the impression tracker. - virtual void Init(Delegate* delegate, InitCallback callback) = 0; - - // Analyzes the impression history for all notification clients, and adjusts - // the |current_max_daily_show|. - virtual void AnalyzeImpressionHistory() = 0; - - // Queries the client states. - virtual void GetClientStates( - std::map<SchedulerClientType, const ClientState*>* client_states) - const = 0; - - virtual ~ImpressionHistoryTracker() = default; - - protected: - ImpressionHistoryTracker() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTracker); -}; - -// An implementation of ImpressionHistoryTracker backed by a database. -class ImpressionHistoryTrackerImpl : public ImpressionHistoryTracker { - public: - explicit ImpressionHistoryTrackerImpl( - const SchedulerConfig& config, - std::unique_ptr<CollectionStore<ClientState>> store); - ~ImpressionHistoryTrackerImpl() override; - - private: - // ImpressionHistoryTracker implementation. - void Init(Delegate* delegate, InitCallback callback) override; - void AnalyzeImpressionHistory() override; - void GetClientStates(std::map<SchedulerClientType, const ClientState*>* - client_states) const override; - void OnClick(const std::string& notification_id) override; - void OnActionClick(const std::string& notification_id, - ActionButtonType button_type) override; - void OnDismiss(const std::string& notification_id) override; - - // Called after |store_| is initialized. - void OnStoreInitialized(InitCallback callback, - bool success, - CollectionStore<ClientState>::Entries entries); - - // Helper method to prune impressions created before |start_time|. Assumes - // |impressions| are sorted by creation time. - static void PruneImpressionByCreateTime(std::deque<Impression*>* impressions, - const base::Time& start_time); - - // Analyzes the impression history for a particular client. - void AnalyzeImpressionHistory(ClientState* client_state); - - // Applies a positive impression result to this notification type. - void ApplyPositiveImpression(ClientState* client_state, - Impression* impression); - - // Applies negative impression on this notification type when |num_actions| - // consecutive negative impression result are generated. - void ApplyNegativeImpressions(ClientState* client_state, - std::deque<Impression*>* impressions, - size_t num_actions); - - // Applies one negative impression. - void ApplyNegativeImpression(ClientState* client_state, - Impression* impression); - - // Checks if suppression is expired and recover to a certain daily quota. - void CheckSuppressionExpiration(ClientState* client_state); - - // Tries to update the database records for |type|. Returns whether the db is - // actually updated. - bool MaybeUpdateDb(SchedulerClientType type); - bool MaybeUpdateAllDb(); - - // Sets/Gets the flag if impression data for |type| needs update in the - // database. - void SetNeedsUpdate(SchedulerClientType type, bool needs_update); - bool NeedsUpdate(SchedulerClientType type) const; - - // Notifies the delegate about impression data update. - void NotifyImpressionUpdate(); - - // Finds an impression that needs to update based on notification id. - Impression* FindImpressionNeedsUpdate(const std::string& notification_guid); - - void OnClickInternal(const std::string& notification_guid, bool update_db); - void OnButtonClickInternal(const std::string& notification_guid, - ActionButtonType button_type, - bool update_db); - void OnDismissInternal(const std::string& notification_guid, bool update_db); - - // Impression history and global states for all notification scheduler - // clients. - ClientStates client_states_; - - // Notification guid to Impression map. - std::map<std::string, Impression*> impression_map_; - - // The storage that persists data. - std::unique_ptr<CollectionStore<ClientState>> store_; - - // System configuration. - const SchedulerConfig& config_; - - // Whether the impression tracker is successfully initialized. - bool initialized_; - - // If the database needs an update when any of the impression data is updated. - std::map<SchedulerClientType, bool> need_update_db_; - - Delegate* delegate_; - - base::WeakPtrFactory<ImpressionHistoryTrackerImpl> weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerImpl); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_HISTORY_TRACKER_H_
diff --git a/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc b/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc deleted file mode 100644 index 0f06d1a..0000000 --- a/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc +++ /dev/null
@@ -1,274 +0,0 @@ -// Copyright 2019 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 <memory> -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/run_loop.h" -#include "base/test/scoped_task_environment.h" -#include "chrome/browser/notifications/scheduler/impression_history_tracker.h" -#include "chrome/browser/notifications/scheduler/test/test_utils.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::_; -using ::testing::Invoke; -using StoreEntries = std::vector<std::unique_ptr<notifications::ClientState>>; - -namespace notifications { -namespace { - -const char kGuid1[] = "guid1"; - -struct TestCase { - // Input data that will be pushed to the target class. - std::vector<test::ImpressionTestData> input; - - // Expected output data. - std::vector<test::ImpressionTestData> expected; -}; - -Impression CreateImpression(const base::Time& create_time, - const std::string& guid) { - return {create_time, - UserFeedback::kNoFeedback, - ImpressionResult::kInvalid, - false /* integrated */, - SchedulerTaskTime::kMorning, - guid, - SchedulerClientType::kTest1}; -} - -TestCase CreateDefaultTestCase() { - TestCase test_case; - test_case.input = {{SchedulerClientType::kTest1, - 2 /* current_max_daily_show */, - {}, - base::nullopt /* suppression_info */}}; - test_case.expected = test_case.input; - return test_case; -} - -class MockImpressionStore : public CollectionStore<ClientState> { - public: - MockImpressionStore() {} - - MOCK_METHOD1(InitAndLoad, void(CollectionStore<ClientState>::LoadCallback)); - MOCK_METHOD3(Add, - void(const std::string&, - const ClientState&, - base::OnceCallback<void(bool)>)); - MOCK_METHOD3(Update, - void(const std::string&, - const ClientState&, - base::OnceCallback<void(bool)>)); - MOCK_METHOD2(Delete, - void(const std::string&, base::OnceCallback<void(bool)>)); - - private: - DISALLOW_COPY_AND_ASSIGN(MockImpressionStore); -}; - -class MockDelegate : public ImpressionHistoryTracker::Delegate { - public: - MockDelegate() = default; - ~MockDelegate() = default; - MOCK_METHOD0(OnImpressionUpdated, void()); - - private: - DISALLOW_COPY_AND_ASSIGN(MockDelegate); -}; - -// TODO(xingliu): Add more test cases following the test doc. -class ImpressionHistoryTrackerTest : public ::testing::Test { - public: - ImpressionHistoryTrackerTest() : store_(nullptr), delegate_(nullptr) {} - ~ImpressionHistoryTrackerTest() override = default; - - void SetUp() override { - config_.impression_expiration = base::TimeDelta::FromDays(28); - config_.suppression_duration = base::TimeDelta::FromDays(56); - } - - protected: - // Creates the tracker and push in data. - void InitTrackerWithData(const TestCase& test_case) { - StoreEntries entries; - test::AddImpressionTestData(test_case.input, &entries); - - auto store = std::make_unique<MockImpressionStore>(); - store_ = store.get(); - delegate_ = std::make_unique<MockDelegate>(); - impression_trakcer_ = std::make_unique<ImpressionHistoryTrackerImpl>( - config_, std::move(store)); - - // Initialize the store and call the callback. - EXPECT_CALL(*store_, InitAndLoad(_)) - .WillOnce( - Invoke([&entries](base::OnceCallback<void(bool, StoreEntries)> cb) { - std::move(cb).Run(true, std::move(entries)); - })); - base::RunLoop loop; - impression_trakcer_->Init( - delegate_.get(), base::BindOnce( - [](base::RepeatingClosure closure, bool success) { - EXPECT_TRUE(success); - std::move(closure).Run(); - }, - loop.QuitClosure())); - loop.Run(); - } - - // Verifies the |expected_test_data| matches the internal states. - void VerifyClientStates(const TestCase& test_case) { - std::map<SchedulerClientType, const ClientState*> client_states; - impression_trakcer_->GetClientStates(&client_states); - - ImpressionHistoryTracker::ClientStates expected_client_states; - test::AddImpressionTestData(test_case.expected, &expected_client_states); - - DCHECK_EQ(expected_client_states.size(), client_states.size()); - for (const auto& expected : expected_client_states) { - auto output_it = client_states.find(expected.first); - DCHECK(output_it != client_states.end()); - EXPECT_EQ(*expected.second, *output_it->second) - << "Unmatch client states: \n" - << "Expected: \n" - << expected.second->DebugPrint() << " \n" - << "Acutual: \n" - << output_it->second->DebugPrint(); - } - } - - const SchedulerConfig& config() const { return config_; } - MockImpressionStore* store() { return store_; } - MockDelegate* delegate() { return delegate_.get(); } - ImpressionHistoryTracker* tracker() { return impression_trakcer_.get(); } - - private: - base::test::ScopedTaskEnvironment scoped_task_environment_; - SchedulerConfig config_; - std::unique_ptr<ImpressionHistoryTracker> impression_trakcer_; - MockImpressionStore* store_; - std::unique_ptr<MockDelegate> delegate_; - - DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerTest); -}; - -// Verifies expired impression will be deleted. -TEST_F(ImpressionHistoryTrackerTest, DeleteExpiredImpression) { - TestCase test_case; - auto expired_create_time = base::Time::Now() - base::TimeDelta::FromDays(1) - - config().impression_expiration; - auto not_expired_time = base::Time::Now() + base::TimeDelta::FromDays(1) - - config().impression_expiration; - Impression expired{expired_create_time, UserFeedback::kNoFeedback, - ImpressionResult::kInvalid, false /* integrated */, - SchedulerTaskTime::kMorning, "guid1", - SchedulerClientType::kTest1}; - Impression not_expired{not_expired_time, - UserFeedback::kNoFeedback, - ImpressionResult::kInvalid, - false /* integrated */, - SchedulerTaskTime::kMorning, - "guid2", - SchedulerClientType::kTest1}; - - // The impressions in the input should be sorted by creation time when gets - // loaded to memory. - test_case.input = {{SchedulerClientType::kTest1, - 2 /* current_max_daily_show */, - {expired, not_expired, expired}, - base::nullopt /* suppression_info */}}; - - // Expired impression created in |expired_create_time| should be deleted. - // No change expected on the next impression, which is not expired and no user - // feedback . - test_case.expected = {{SchedulerClientType::kTest1, - 2 /* current_max_daily_show */, - {not_expired}, - base::nullopt /* suppression_info */}}; - - InitTrackerWithData(test_case); - EXPECT_CALL(*store(), Update(_, _, _)); - EXPECT_CALL(*delegate(), OnImpressionUpdated()); - tracker()->AnalyzeImpressionHistory(); - VerifyClientStates(test_case); -} - -// If impression has been deleted, click should have no result. -TEST_F(ImpressionHistoryTrackerTest, ClickNoImpression) { - TestCase test_case = CreateDefaultTestCase(); - InitTrackerWithData(test_case); - EXPECT_CALL(*store(), Update(_, _, _)).Times(0); - EXPECT_CALL(*delegate(), OnImpressionUpdated()).Times(0); - tracker()->OnClick(kGuid1); - VerifyClientStates(test_case); -} - -struct UserActionTestParam { - ImpressionResult impression_result = ImpressionResult::kInvalid; - UserFeedback user_feedback = UserFeedback::kNoFeedback; - int current_max_daily_show = 0; - base::Optional<ActionButtonType> button_type; - base::Optional<SuppressionInfo> suppression_info; -}; - -class ImpressionHistoryTrackerUserActionTest - : public ImpressionHistoryTrackerTest, - public ::testing::WithParamInterface<UserActionTestParam> { - public: - ImpressionHistoryTrackerUserActionTest() = default; - ~ImpressionHistoryTrackerUserActionTest() override = default; - - private: - DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerUserActionTest); -}; - -// TODO(xingliu): Add test for unhelpful/dismiss, need to use base::Clock to -// mock base::Time::Now(). -const UserActionTestParam kUserActionTestParams[] = { - {ImpressionResult::kPositive, UserFeedback::kClick, 3, base::nullopt, - base::nullopt}, - {ImpressionResult::kPositive, UserFeedback::kHelpful, 3, - ActionButtonType::kHelpful, base::nullopt}}; - -// User actions like clicks should update the ClientState data accordingly. -TEST_P(ImpressionHistoryTrackerUserActionTest, UserAction) { - TestCase test_case = CreateDefaultTestCase(); - Impression impression = CreateImpression(base::Time::Now(), kGuid1); - DCHECK(!test_case.input.empty()); - test_case.input.front().impressions.emplace_back(impression); - - impression.impression = GetParam().impression_result; - impression.integrated = true; - impression.feedback = GetParam().user_feedback; - - test_case.expected.front().current_max_daily_show = - GetParam().current_max_daily_show; - test_case.expected.front().impressions.emplace_back(impression); - test_case.expected.front().suppression_info = GetParam().suppression_info; - - InitTrackerWithData(test_case); - EXPECT_CALL(*store(), Update(_, _, _)); - EXPECT_CALL(*delegate(), OnImpressionUpdated()); - - // Trigger user action. - if (GetParam().user_feedback == UserFeedback::kClick) - tracker()->OnClick(kGuid1); - else if (GetParam().button_type.has_value()) - tracker()->OnActionClick(kGuid1, GetParam().button_type.value()); - - VerifyClientStates(test_case); -} - -INSTANTIATE_TEST_SUITE_P(, - ImpressionHistoryTrackerUserActionTest, - testing::ValuesIn(kUserActionTestParams)); - -} // namespace - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_store.cc b/chrome/browser/notifications/scheduler/impression_store.cc deleted file mode 100644 index 8b79c62..0000000 --- a/chrome/browser/notifications/scheduler/impression_store.cc +++ /dev/null
@@ -1,100 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/impression_store.h" - -#include "chrome/browser/notifications/scheduler/proto_conversion.h" - -namespace leveldb_proto { - -void DataToProto(notifications::ClientState* client_state, - notifications::proto::ClientState* proto) { - ClientStateToProto(client_state, proto); -} - -void ProtoToData(notifications::proto::ClientState* proto, - notifications::ClientState* client_state) { - ClientStateFromProto(proto, client_state); -} - -} // namespace leveldb_proto - -namespace notifications { - -ImpressionStore::ImpressionStore( - std::unique_ptr< - leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>> db) - : db_(std::move(db)), weak_ptr_factory_(this) {} - -ImpressionStore::~ImpressionStore() = default; - -void ImpressionStore::InitAndLoad(LoadCallback callback) { - db_->Init(base::BindOnce(&ImpressionStore::OnDbInitialized, - weak_ptr_factory_.GetWeakPtr(), - std::move(callback))); -} - -void ImpressionStore::OnDbInitialized(LoadCallback callback, - leveldb_proto::Enums::InitStatus status) { - if (status != leveldb_proto::Enums::InitStatus::kOK) { - std::move(callback).Run(false, Entries()); - return; - } - - // Load the data after a successful initialization. - db_->LoadEntries(base::BindOnce(&ImpressionStore::OnDataLoaded, - weak_ptr_factory_.GetWeakPtr(), - std::move(callback))); -} - -void ImpressionStore::OnDataLoaded(LoadCallback callback, - bool success, - std::unique_ptr<EntryVector> entry_vector) { - // The database failed to load. - if (!success) { - std::move(callback).Run(false, Entries()); - return; - } - - // Success to load but no data. - if (!entry_vector) { - std::move(callback).Run(true, Entries()); - return; - } - - // Load data. - Entries entries; - for (auto it = entry_vector->begin(); it != entry_vector->end(); ++it) { - std::unique_ptr<ClientState> client_state = std::make_unique<ClientState>(); - *client_state = std::move(*it); - entries.emplace_back(std::move(client_state)); - } - - std::move(callback).Run(true, std::move(entries)); -} - -void ImpressionStore::Add(const std::string& key, - const ClientState& client_state, - UpdateCallback callback) { - Update(key, client_state, std::move(callback)); -} - -void ImpressionStore::Update(const std::string& key, - const ClientState& client_state, - UpdateCallback callback) { - auto entries_to_save = std::make_unique<KeyEntryVector>(); - entries_to_save->emplace_back(std::make_pair(key, client_state)); - db_->UpdateEntries(std::move(entries_to_save), - std::make_unique<KeyVector>() /*keys_to_remove*/, - std::move(callback)); -} - -void ImpressionStore::Delete(const std::string& key, UpdateCallback callback) { - auto keys_to_delete = std::make_unique<KeyVector>(); - keys_to_delete->emplace_back(key); - db_->UpdateEntries(std::make_unique<KeyEntryVector>() /*entries_to_save*/, - std::move(keys_to_delete), std::move(callback)); -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_store.h b/chrome/browser/notifications/scheduler/impression_store.h deleted file mode 100644 index 3749720a..0000000 --- a/chrome/browser/notifications/scheduler/impression_store.h +++ /dev/null
@@ -1,73 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_IMPRESSION_STORE_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_STORE_H_ - -#include <memory> -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "chrome/browser/notifications/proto/client_state.pb.h" -#include "chrome/browser/notifications/scheduler/collection_store.h" -#include "chrome/browser/notifications/scheduler/impression_types.h" -#include "components/leveldb_proto/public/proto_database.h" - -// Forward declaration for proto conversion. -namespace leveldb_proto { -void DataToProto(notifications::ClientState* client_state, - notifications::proto::ClientState* proto); - -void ProtoToData(notifications::proto::ClientState* proto, - notifications::ClientState* client_state); -} // namespace leveldb_proto - -namespace notifications { - -// An impression storage using a proto database to persist data. -class ImpressionStore : public CollectionStore<ClientState> { - public: - ImpressionStore( - std::unique_ptr< - leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>> db); - ~ImpressionStore() override; - - private: - using KeyEntryVector = std::vector<std::pair<std::string, ClientState>>; - using KeyVector = std::vector<std::string>; - using EntryVector = std::vector<ClientState>; - - // CollectionStore implementation. - void InitAndLoad(LoadCallback callback) override; - void Add(const std::string& key, - const ClientState& client_state, - UpdateCallback callback) override; - void Update(const std::string& key, - const ClientState& client_state, - UpdateCallback callback) override; - void Delete(const std::string& key, UpdateCallback callback) override; - - // Called when the proto database is initialized but no yet loading the data - // into memory. - void OnDbInitialized(LoadCallback callback, - leveldb_proto::Enums::InitStatus status); - - // Called after loading the data from database. - void OnDataLoaded(LoadCallback callback, - bool success, - std::unique_ptr<EntryVector> entry_vector); - - std::unique_ptr<leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>> - db_; - base::WeakPtrFactory<ImpressionStore> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(ImpressionStore); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/impression_store_unittest.cc b/chrome/browser/notifications/scheduler/impression_store_unittest.cc deleted file mode 100644 index 4872afe..0000000 --- a/chrome/browser/notifications/scheduler/impression_store_unittest.cc +++ /dev/null
@@ -1,207 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/impression_store.h" - -#include <map> -#include <memory> -#include <string> -#include <utility> - -#include "base/test/scoped_task_environment.h" -#include "chrome/browser/notifications/proto/client_state.pb.h" -#include "chrome/browser/notifications/scheduler/impression_types.h" -#include "chrome/browser/notifications/scheduler/proto_conversion.h" -#include "components/leveldb_proto/public/proto_database.h" -#include "components/leveldb_proto/testing/fake_db.h" -#include "testing/gtest/include/gtest/gtest.h" - -using leveldb_proto::test::FakeDB; -using InitStatus = leveldb_proto::Enums::InitStatus; -using Entries = notifications::ImpressionStore::Entries; -using DbEntries = std::vector<notifications::ClientState>; -using DbEntriesPtr = std::unique_ptr<std::vector<notifications::ClientState>>; -using TestClientStates = std::map<std::string, notifications::ClientState>; - -namespace notifications { -namespace { - -const char kClientStateKey[] = "guid_client_state_key1"; -const ClientState kDefaultClientState; - -// Test fixture to verify impression store. -class ImpressionStoreTest : public testing::Test { - public: - ImpressionStoreTest() : load_result_(false), db_(nullptr) {} - ~ImpressionStoreTest() override = default; - - void SetUp() override {} - - protected: - // Initialize the store with test data. - void Init(const TestClientStates& test_data, InitStatus status) { - CreateTestProto(test_data); - - auto db = - std::make_unique<FakeDB<proto::ClientState, ClientState>>(&db_entries_); - db_ = db.get(); - store_ = std::make_unique<ImpressionStore>(std::move(db)); - store_->InitAndLoad(base::BindOnce(&ImpressionStoreTest::OnEntriesLoaded, - base::Unretained(this))); - db_->InitStatusCallback(status); - } - - bool load_result() const { return load_result_; } - const Entries& loaded_entries() const { return loaded_entries_; } - FakeDB<proto::ClientState, ClientState>* db() { return db_; } - - CollectionStore<ClientState>* store() { return store_.get(); } - - void OnEntriesLoaded(bool success, Entries entries) { - loaded_entries_ = std::move(entries); - load_result_ = success; - } - - // Verifies the entries in the db is |expected|. - void VerifyDataInDb(DbEntriesPtr expected) { - db_->LoadEntries(base::BindOnce( - [](DbEntriesPtr expected, bool success, DbEntriesPtr entries) { - EXPECT_TRUE(success); - DCHECK(entries); - DCHECK(expected); - EXPECT_EQ(entries->size(), expected->size()); - for (size_t i = 0, size = entries->size(); i < size; ++i) { - EXPECT_EQ((*entries)[i], (*expected)[i]); - } - }, - std::move(expected))); - db_->LoadCallback(true); - } - - private: - void CreateTestProto(const TestClientStates& client_states) { - for (const auto& pair : client_states) { - auto client_state(pair.second); - auto key = pair.first; - notifications::proto::ClientState proto; - ClientStateToProto(&client_state, &proto); - db_entries_.emplace(key, proto); - } - } - - base::test::ScopedTaskEnvironment scoped_task_environment_; - std::map<std::string, proto::ClientState> db_entries_; - bool load_result_; - Entries loaded_entries_; - - FakeDB<proto::ClientState, ClientState>* db_; - std::unique_ptr<CollectionStore<ClientState>> store_; - - DISALLOW_COPY_AND_ASSIGN(ImpressionStoreTest); -}; - -// Initializes an empty database should success. -TEST_F(ImpressionStoreTest, InitSuccessEmptyDb) { - Init(TestClientStates(), InitStatus::kOK); - db()->LoadCallback(true); - EXPECT_EQ(load_result(), true); - EXPECT_TRUE(loaded_entries().empty()); -} - -// Initialize non-empty database should success. -TEST_F(ImpressionStoreTest, InitSuccessWithData) { - auto test_data = TestClientStates(); - test_data.emplace(kClientStateKey, kDefaultClientState); - Init(test_data, InitStatus::kOK); - db()->LoadCallback(true); - EXPECT_EQ(load_result(), true); - EXPECT_EQ(loaded_entries().size(), 1u); - EXPECT_EQ(*loaded_entries().back(), kDefaultClientState); -} - -// Failure when loading the data will result in error. -TEST_F(ImpressionStoreTest, InitSuccessLoadFailed) { - Init(TestClientStates(), InitStatus::kOK); - db()->LoadCallback(false); - EXPECT_EQ(load_result(), false); - EXPECT_TRUE(loaded_entries().empty()); -} - -// Failed database initialization will result in error. -TEST_F(ImpressionStoreTest, InitFailed) { - Init(TestClientStates(), InitStatus::kCorrupt); - EXPECT_EQ(load_result(), false); - EXPECT_TRUE(loaded_entries().empty()); -} - -// Verifies adding data. -TEST_F(ImpressionStoreTest, Add) { - Init(TestClientStates(), InitStatus::kOK); - db()->LoadCallback(true); - EXPECT_EQ(load_result(), true); - EXPECT_TRUE(loaded_entries().empty()); - - // Add data to the store. - store()->Add(kClientStateKey, kDefaultClientState, - base::BindOnce([](bool success) { EXPECT_TRUE(success); })); - db()->UpdateCallback(true); - - // Verify the new data is in the database. - auto expected = std::make_unique<DbEntries>(); - expected->emplace_back(kDefaultClientState); - VerifyDataInDb(std::move(expected)); -} - -// Verifies failure when adding data will result in error. -TEST_F(ImpressionStoreTest, AddFailed) { - Init(TestClientStates(), InitStatus::kOK); - db()->LoadCallback(true); - EXPECT_EQ(load_result(), true); - EXPECT_TRUE(loaded_entries().empty()); - - store()->Add(kClientStateKey, kDefaultClientState, - base::BindOnce([](bool success) { EXPECT_FALSE(success); })); - db()->UpdateCallback(false); -} - -// Verifies updating data. -TEST_F(ImpressionStoreTest, Update) { - auto test_data = TestClientStates(); - test_data.emplace(kClientStateKey, kDefaultClientState); - Init(test_data, InitStatus::kOK); - db()->LoadCallback(true); - EXPECT_EQ(load_result(), true); - - ClientState new_client_state; - new_client_state.current_max_daily_show = 100; - - // Update the database. - store()->Update(kClientStateKey, new_client_state, - base::BindOnce([](bool success) { EXPECT_TRUE(success); })); - db()->UpdateCallback(true); - - // Verify the updated data is in the database. - auto expected = std::make_unique<DbEntries>(); - expected->emplace_back(new_client_state); - VerifyDataInDb(std::move(expected)); -} - -// Verifies deleting data. -TEST_F(ImpressionStoreTest, Delete) { - auto test_data = TestClientStates(); - test_data.emplace(kClientStateKey, kDefaultClientState); - Init(test_data, InitStatus::kOK); - db()->LoadCallback(true); - EXPECT_EQ(load_result(), true); - - // Delete the entry. - store()->Delete(kClientStateKey, - base::BindOnce([](bool success) { EXPECT_TRUE(success); })); - - // Verify there is no data in the database. - VerifyDataInDb(std::make_unique<DbEntries>()); -} - -} // namespace -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_types.cc b/chrome/browser/notifications/scheduler/impression_types.cc deleted file mode 100644 index 12eeaf5..0000000 --- a/chrome/browser/notifications/scheduler/impression_types.cc +++ /dev/null
@@ -1,86 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/impression_types.h" - -#include <sstream> - -#include "base/format_macros.h" -#include "base/strings/stringprintf.h" - -namespace notifications { - -bool Impression::operator==(const Impression& other) const { - return create_time == other.create_time && feedback == other.feedback && - impression == other.impression && integrated == other.integrated && - task_start_time == other.task_start_time && guid == other.guid && - type == other.type; -} - -SuppressionInfo::SuppressionInfo(const base::Time& last_trigger, - const base::TimeDelta& duration) - : last_trigger_time(last_trigger), duration(duration), recover_goal(1) {} - -SuppressionInfo::SuppressionInfo(const SuppressionInfo& other) = default; - -bool SuppressionInfo::operator==(const SuppressionInfo& other) const { - return last_trigger_time == other.last_trigger_time && - duration == other.duration && recover_goal == other.recover_goal; -} - -base::Time SuppressionInfo::ReleaseTime() const { - return last_trigger_time + duration; -} - -ClientState::ClientState() - : type(SchedulerClientType::kUnknown), current_max_daily_show(0) {} - -ClientState::ClientState(const ClientState& other) = default; - -ClientState::~ClientState() = default; - -bool ClientState::operator==(const ClientState& other) const { - return type == other.type && - current_max_daily_show == other.current_max_daily_show && - impressions == other.impressions && - suppression_info == other.suppression_info; -} - -std::string ClientState::DebugPrint() const { - std::string log = base::StringPrintf( - "Client state: type: %d \n" - "current_max_daily_show: %d \n" - "impressions.size(): %zu \n", - static_cast<int>(type), current_max_daily_show, impressions.size()); - - for (const auto& impression : impressions) { - std::ostringstream stream; - stream << "Impression, create_time:" << impression.create_time << "\n" - << " create_time in microseconds:" - << impression.create_time.ToDeltaSinceWindowsEpoch().InMicroseconds() - << "\n" - << "feedback: " << static_cast<int>(impression.feedback) << "\n" - << "impression result: " << static_cast<int>(impression.impression) - << " \n" - << "integrated: " << impression.integrated << "\n" - << "task start time: " - << static_cast<int>(impression.task_start_time) << "\n" - << "guid: " << impression.guid << "\n" - << "type: " << static_cast<int>(impression.type); - log += stream.str(); - } - - if (suppression_info.has_value()) { - std::ostringstream stream; - stream << "Suppression info, last_trigger_time:" - << suppression_info->last_trigger_time << "\n" - << "duration:" << suppression_info->duration << "\n" - << "recover_goal:" << suppression_info->recover_goal; - log += stream.str(); - } - - return log; -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/impression_types.h b/chrome/browser/notifications/scheduler/impression_types.h deleted file mode 100644 index 2399547..0000000 --- a/chrome/browser/notifications/scheduler/impression_types.h +++ /dev/null
@@ -1,110 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_IMPRESSION_TYPES_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_TYPES_H_ - -#include <deque> -#include <map> -#include <string> - -#include "base/optional.h" -#include "base/time/time.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" - -namespace notifications { - -// Contains data to determine when a notification should be shown to the user -// and the user impression towards this notification. -// -// Life cycle: -// 1. Created after the notification is shown to the user. -// 2. |feedback| is set after the user interacts with the notification. -// 3. Notification scheduler API consumer gets the user feedback and generates -// an impression result, which may affect notification exposure. -// 4. The impression is deleted after it expires. -struct Impression { - bool operator==(const Impression& other) const; - - // Creation timestamp. - base::Time create_time; - - // The user feedback on the notification, each notification will have at most - // one feedback. Sets after the user interacts with the notification. - UserFeedback feedback = UserFeedback::kNoFeedback; - - // The impression type. The client of a notification type takes one or several - // user feedbacks as input and generate a user impression, which will - // eventually affect the rate to deliver notifications to the user. - ImpressionResult impression = ImpressionResult::kInvalid; - - // If the user feedback is used in computing the current notification deliver - // rate. - bool integrated = false; - - // The task start time when this impression is generated. - SchedulerTaskTime task_start_time = SchedulerTaskTime::kUnknown; - - // The unique identifier of the notification. - std::string guid; - - // The type of the notification. Not persisted to disk, set after database - // initialized. - // TODO(xingliu): Consider to persist this as well. - SchedulerClientType type = SchedulerClientType::kUnknown; -}; - -// Contains details about supression and recovery after suppression expired. -struct SuppressionInfo { - SuppressionInfo(const base::Time& last_trigger, - const base::TimeDelta& duration); - SuppressionInfo(const SuppressionInfo& other); - ~SuppressionInfo() = default; - bool operator==(const SuppressionInfo& other) const; - - // Time that the suppression should release. - base::Time ReleaseTime() const; - - // The last supression trigger time. - base::Time last_trigger_time; - - // The duration for the suppression. - base::TimeDelta duration; - - // |current_max_daily_show| will change to this after the suppression - // expired. - int recover_goal; -}; - -// Stores the global states about how often the notification can be shown -// to the user and the history of user interactions to a particular notification -// client. -struct ClientState { - using Impressions = std::deque<Impression>; - ClientState(); - explicit ClientState(const ClientState& other); - ~ClientState(); - - bool operator==(const ClientState& other) const; - - // Dumps data for debugging. - std::string DebugPrint() const; - - // The type of notification using the scheduler. - SchedulerClientType type; - - // The maximum number of notifications shown to the user for this type. May - // change if the user interacts with the notification. - int current_max_daily_show; - - // A list of user impression history. Sorted by creation time. - Impressions impressions; - - // Suppression details, no value if there is currently no suppression. - base::Optional<SuppressionInfo> suppression_info; -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_IMPRESSION_TYPES_H_
diff --git a/chrome/browser/notifications/scheduler/init_aware_scheduler.cc b/chrome/browser/notifications/scheduler/init_aware_scheduler.cc deleted file mode 100644 index 965bf3a..0000000 --- a/chrome/browser/notifications/scheduler/init_aware_scheduler.cc +++ /dev/null
@@ -1,56 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/init_aware_scheduler.h" - -#include "base/bind.h" -#include "chrome/browser/notifications/scheduler/notification_params.h" - -namespace notifications { - -InitAwareNotificationScheduler::InitAwareNotificationScheduler( - std::unique_ptr<NotificationScheduler> impl) - : impl_(std::move(impl)), weak_ptr_factory_(this) {} - -InitAwareNotificationScheduler::~InitAwareNotificationScheduler() = default; - -void InitAwareNotificationScheduler::Init(InitCallback init_callback) { - DCHECK(!init_success_.has_value()); - impl_->Init(base::BindOnce(&InitAwareNotificationScheduler::OnInitialized, - weak_ptr_factory_.GetWeakPtr(), - std::move(init_callback))); -} -void InitAwareNotificationScheduler::Schedule( - std::unique_ptr<NotificationParams> params) { - if (init_success_.has_value() && *init_success_) { - impl_->Schedule(std::move(params)); - return; - } - - if (init_success_.has_value() && !*init_success_) - return; - - cached_closures_.emplace_back( - base::BindOnce(&InitAwareNotificationScheduler::Schedule, - weak_ptr_factory_.GetWeakPtr(), std::move(params))); -} - -void InitAwareNotificationScheduler::OnInitialized(InitCallback init_callback, - bool success) { - init_success_ = success; - if (!success) { - cached_closures_.clear(); - std::move(init_callback).Run(false); - return; - } - - // Flush all cached calls. - for (auto it = cached_closures_.begin(); it != cached_closures_.end(); ++it) { - std::move(*it).Run(); - } - cached_closures_.clear(); - std::move(init_callback).Run(true); -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/init_aware_scheduler.h b/chrome/browser/notifications/scheduler/init_aware_scheduler.h deleted file mode 100644 index df8c61d..0000000 --- a/chrome/browser/notifications/scheduler/init_aware_scheduler.h +++ /dev/null
@@ -1,55 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INIT_AWARE_SCHEDULER_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INIT_AWARE_SCHEDULER_H_ - -#include <memory> -#include <vector> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/optional.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler.h" - -namespace notifications { - -struct NotificationParams; - -// NotificationScheduler implementation that caches NotificationScheduler API -// calls and flush them to the actual implementation after initialization -// succeeded. -class InitAwareNotificationScheduler : public NotificationScheduler { - public: - explicit InitAwareNotificationScheduler( - std::unique_ptr<NotificationScheduler> impl); - ~InitAwareNotificationScheduler() override; - - private: - // NotificationScheduler implementation. - void Init(InitCallback init_callback) override; - void Schedule( - std::unique_ptr<NotificationParams> notification_params) override; - - // Called after initialization is done. - void OnInitialized(InitCallback init_callback, bool success); - - // Whether the initialization is successful. No value if initialization is not - // finished. - base::Optional<bool> init_success_; - - // Cached calls. - std::vector<base::OnceClosure> cached_closures_; - - // Actual implementation. - std::unique_ptr<NotificationScheduler> impl_; - - base::WeakPtrFactory<InitAwareNotificationScheduler> weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(InitAwareNotificationScheduler); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INIT_AWARE_SCHEDULER_H_
diff --git a/chrome/browser/notifications/scheduler/init_aware_scheduler_unittest.cc b/chrome/browser/notifications/scheduler/init_aware_scheduler_unittest.cc deleted file mode 100644 index 107be6a8..0000000 --- a/chrome/browser/notifications/scheduler/init_aware_scheduler_unittest.cc +++ /dev/null
@@ -1,127 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/init_aware_scheduler.h" - -#include <memory> - -#include "base/bind_helpers.h" -#include "base/test/scoped_task_environment.h" -#include "chrome/browser/notifications/scheduler/notification_params.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::_; -using testing::InSequence; -using testing::Invoke; - -namespace notifications { -namespace { - -class MockNotificationScheduler : public NotificationScheduler { - public: - MockNotificationScheduler() = default; - ~MockNotificationScheduler() override = default; - - MOCK_METHOD1(Init, void(InitCallback)); - MOCK_METHOD1(Schedule, void(std::unique_ptr<NotificationParams>)); - - private: - DISALLOW_COPY_AND_ASSIGN(MockNotificationScheduler); -}; - -class InitAwareNotificationSchedulerTest : public testing::Test { - public: - InitAwareNotificationSchedulerTest() : scheduler_impl_(nullptr) {} - ~InitAwareNotificationSchedulerTest() override = default; - - void SetUp() override { - auto scheduler = std::make_unique<MockNotificationScheduler>(); - scheduler_impl_ = scheduler.get(); - init_aware_scheduler_ = - std::make_unique<InitAwareNotificationScheduler>(std::move(scheduler)); - } - - protected: - std::unique_ptr<NotificationParams> BuildParams() { - return std::make_unique<NotificationParams>( - SchedulerClientType::kUnknown, NotificationData(), ScheduleParams()); - } - - NotificationScheduler* init_aware_scheduler() { - return init_aware_scheduler_.get(); - } - MockNotificationScheduler* scheduler_impl() { return scheduler_impl_; } - - private: - base::test::ScopedTaskEnvironment scoped_task_environment_; - MockNotificationScheduler* scheduler_impl_; - std::unique_ptr<NotificationScheduler> init_aware_scheduler_; - - DISALLOW_COPY_AND_ASSIGN(InitAwareNotificationSchedulerTest); -}; - -// Checks std::unique_ptr<NotificationParams> has specific guid. -MATCHER_P(GuidIs, expected_guid, "") { - return arg->guid == expected_guid; -} - -// Verifies cached calls are flushed into the actual implementation. -TEST_F(InitAwareNotificationSchedulerTest, FlushCachedCalls) { - auto params = BuildParams(); - std::string guid = params->guid; - EXPECT_FALSE(guid.empty()); - { - InSequence sequence; - EXPECT_CALL(*scheduler_impl(), Init(_)) - .WillOnce(Invoke([](NotificationScheduler::InitCallback cb) { - std::move(cb).Run(true /*success*/); - })); - EXPECT_CALL(*scheduler_impl(), Schedule(GuidIs(guid))); - - // Schedule() call before Init() will be cached. - init_aware_scheduler()->Schedule(std::move(params)); - init_aware_scheduler()->Init(base::DoNothing()); - } -} - -// Verifies that API calls after successful initialization will call into the -// actual implementation. -TEST_F(InitAwareNotificationSchedulerTest, CallAfterInitSuccess) { - auto params = BuildParams(); - std::string guid = params->guid; - EXPECT_FALSE(guid.empty()); - { - InSequence sequence; - EXPECT_CALL(*scheduler_impl(), Init(_)) - .WillOnce(Invoke([](NotificationScheduler::InitCallback cb) { - std::move(cb).Run(true /*success*/); - })); - EXPECT_CALL(*scheduler_impl(), Schedule(GuidIs(guid))); - - // Schedule() call after Init(). - init_aware_scheduler()->Init(base::DoNothing()); - init_aware_scheduler()->Schedule(std::move(params)); - } -} - -// Verifies no calls are flushed to actual implementation if initialization -// failed. -TEST_F(InitAwareNotificationSchedulerTest, NoFlushOnInitFailure) { - auto params1 = BuildParams(); - auto params2 = BuildParams(); - - EXPECT_CALL(*scheduler_impl(), Init(_)) - .WillOnce(Invoke([](NotificationScheduler::InitCallback cb) { - std::move(cb).Run(false /*success*/); - })); - EXPECT_CALL(*scheduler_impl(), Schedule(_)).Times(0); - - init_aware_scheduler()->Schedule(std::move(params1)); - init_aware_scheduler()->Init(base::DoNothing()); - init_aware_scheduler()->Schedule(std::move(params2)); -} - -} // namespace -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/BUILD.gn b/chrome/browser/notifications/scheduler/internal/BUILD.gn new file mode 100644 index 0000000..c8638e0 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/BUILD.gn
@@ -0,0 +1,96 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/buildflag_header.gni") + +if (is_android) { + import("//build/config/android/rules.gni") +} + +# Internal library that embedders should not directly depend on. +source_set("lib") { + visibility = [ + ":unit_tests", + "//chrome/browser/notifications/scheduler", + "//chrome/browser/notifications/scheduler:factory", + "//chrome/browser/notifications/scheduler/test:test_lib", + ] + + sources = [ + "background_task_coordinator.cc", + "background_task_coordinator.h", + "collection_store.h", + "display_decider.cc", + "display_decider.h", + "distribution_policy.cc", + "distribution_policy.h", + "icon_entry.cc", + "icon_entry.h", + "icon_store.cc", + "icon_store.h", + "impression_history_tracker.cc", + "impression_history_tracker.h", + "impression_store.cc", + "impression_store.h", + "impression_types.cc", + "impression_types.h", + "init_aware_scheduler.cc", + "init_aware_scheduler.h", + "notification_entry.cc", + "notification_entry.h", + "notification_schedule_service_impl.cc", + "notification_schedule_service_impl.h", + "notification_scheduler.cc", + "notification_scheduler.h", + "notification_scheduler_context.cc", + "notification_scheduler_context.h", + "notification_store.cc", + "notification_store.h", + "proto_conversion.cc", + "proto_conversion.h", + "scheduled_notification_manager.cc", + "scheduled_notification_manager.h", + "scheduler_config.cc", + "scheduler_config.h", + "scheduler_utils.cc", + "scheduler_utils.h", + ] + + # This target should not depend on anything in //chrome/* except the proto library. + deps = [ + "//base", + "//chrome/browser/notifications/proto", + "//chrome/browser/notifications/scheduler/public", + "//components/keyed_service/core", + "//components/leveldb_proto", + "//skia", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "background_task_coordinator_unittest.cc", + "display_decider_unittest.cc", + "distribution_policy_unittest.cc", + "icon_store_unittest.cc", + "impression_history_tracker_unittest.cc", + "impression_store_unittest.cc", + "init_aware_scheduler_unittest.cc", + "notification_store_unittest.cc", + "proto_conversion_unittest.cc", + "scheduled_notification_manager_unittest.cc", + "scheduler_utils_unittest.cc", + ] + + deps = [ + "//chrome/browser/notifications/proto", + "//chrome/browser/notifications/scheduler/internal:lib", + "//chrome/browser/notifications/scheduler/public", + "//chrome/browser/notifications/scheduler/test:test_lib", + "//components/leveldb_proto:test_support", + "//testing/gmock", + "//testing/gtest", + ] +}
diff --git a/chrome/browser/notifications/scheduler/internal/background_task_coordinator.cc b/chrome/browser/notifications/scheduler/internal/background_task_coordinator.cc new file mode 100644 index 0000000..95bfa6c --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/background_task_coordinator.cc
@@ -0,0 +1,165 @@ +// Copyright 2019 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/notifications/scheduler/internal/background_task_coordinator.h" + +#include <algorithm> +#include <utility> + +#include "base/numerics/ranges.h" +#include "base/optional.h" +#include "base/time/clock.h" +#include "chrome/browser/notifications/scheduler/internal/impression_types.h" +#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h" +#include "chrome/browser/notifications/scheduler/internal/scheduler_utils.h" +#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h" + +namespace notifications { +namespace { + +class BackgroundTaskCoordinatorHelper { + public: + BackgroundTaskCoordinatorHelper( + NotificationBackgroundTaskScheduler* background_task, + const SchedulerConfig* config, + base::Clock* clock) + : background_task_(background_task), config_(config), clock_(clock) {} + ~BackgroundTaskCoordinatorHelper() = default; + + void ScheduleBackgroundTask( + BackgroundTaskCoordinator::Notifications notifications, + BackgroundTaskCoordinator::ClientStates client_states, + SchedulerTaskTime task_start_time) { + if (notifications.empty()) { + background_task_->Cancel(); + return; + } + + std::map<SchedulerClientType, int> shown_per_type; + int shown_total = 0; + SchedulerClientType last_shown_type = SchedulerClientType::kUnknown; + NotificationsShownToday(client_states, &shown_per_type, &shown_total, + &last_shown_type); + bool reach_max_today_all_type = + shown_total >= config_->max_daily_shown_all_type; + base::Time next_morning = NextMorning(); + base::Time this_evening = ThisEvening(); + + for (const auto& pair : notifications) { + auto type = pair.first; + auto it = client_states.find(type); + if (pair.second.empty() || (it == client_states.end())) + continue; + + const ClientState* client_state = it->second; + + // Try to schedule on the day that suppression expires. + if (client_state->suppression_info.has_value()) { + const auto& suppression = client_state->suppression_info.value(); + base::Time suppression_expire; + ToLocalHour(config_->morning_task_hour, suppression.ReleaseTime(), + 0 /*day_delta*/, &suppression_expire); + MaybeUpdateBackgroundTaskTime( + std::max(suppression_expire, next_morning)); + continue; + } + + // Has met the quota for this notification type or for all types, only can + // send more on next day. + bool reach_max_today = + shown_per_type[type] >= client_state->current_max_daily_show; + if (reach_max_today || reach_max_today_all_type) { + MaybeUpdateBackgroundTaskTime(next_morning); + continue; + } + + switch (task_start_time) { + case SchedulerTaskTime::kMorning: + // Still can send more in the evening. + MaybeUpdateBackgroundTaskTime(this_evening); + break; + case SchedulerTaskTime::kEvening: + // Wait until the next calendar day. + MaybeUpdateBackgroundTaskTime(next_morning); + break; + case SchedulerTaskTime::kUnknown: + // TODO(xingliu): Support arbitrary time background task. + NOTIMPLEMENTED(); + break; + } + } + + ScheduleBackgroundTaskInternal(task_start_time); + } + + private: + // Returns the morning background task time on the next day. + base::Time NextMorning() { + base::Time next_morning; + bool success = ToLocalHour(config_->morning_task_hour, clock_->Now(), + 1 /*day_delta*/, &next_morning); + DCHECK(success); + return next_morning; + } + + // Returns the evening background task time on today. + base::Time ThisEvening() { + base::Time this_evening; + bool success = ToLocalHour(config_->evening_task_hour, clock_->Now(), + 0 /*day_delta*/, &this_evening); + DCHECK(success); + return this_evening; + } + + void MaybeUpdateBackgroundTaskTime(const base::Time& time) { + if (!background_task_time_.has_value() || + time < background_task_time_.value()) + background_task_time_ = time; + } + + void ScheduleBackgroundTaskInternal(SchedulerTaskTime task_start_time) { + if (!background_task_time_.has_value()) + return; + + base::TimeDelta window_start_time = + background_task_time_.value() - clock_->Now(); + window_start_time = base::ClampToRange(window_start_time, base::TimeDelta(), + base::TimeDelta::Max()); + + background_task_->Schedule( + task_start_time, window_start_time, + window_start_time + config_->background_task_window_duration); + } + + NotificationBackgroundTaskScheduler* background_task_; + const SchedulerConfig* config_; + base::Clock* clock_; + base::Optional<base::Time> background_task_time_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinatorHelper); +}; + +} // namespace + +BackgroundTaskCoordinator::BackgroundTaskCoordinator( + std::unique_ptr<NotificationBackgroundTaskScheduler> background_task, + const SchedulerConfig* config, + base::Clock* clock) + : background_task_(std::move(background_task)), + config_(config), + clock_(clock) {} + +BackgroundTaskCoordinator::~BackgroundTaskCoordinator() = default; + +void BackgroundTaskCoordinator::ScheduleBackgroundTask( + Notifications notifications, + ClientStates client_states, + SchedulerTaskTime task_start_time) { + auto helper = std::make_unique<BackgroundTaskCoordinatorHelper>( + background_task_.get(), config_, clock_); + helper->ScheduleBackgroundTask(std::move(notifications), + std::move(client_states), task_start_time); +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/background_task_coordinator.h b/chrome/browser/notifications/scheduler/internal/background_task_coordinator.h new file mode 100644 index 0000000..f2001d2 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/background_task_coordinator.h
@@ -0,0 +1,59 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_BACKGROUND_TASK_COORDINATOR_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_BACKGROUND_TASK_COORDINATOR_H_ + +#include <map> +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" + +namespace base { +class Clock; +} // namespace base + +namespace notifications { + +class NotificationBackgroundTaskScheduler; +struct ClientState; +struct NotificationEntry; +struct SchedulerConfig; + +// Schedules background task at the right time based on scheduled notification +// data and impression data. +class BackgroundTaskCoordinator { + public: + using Notifications = + std::map<SchedulerClientType, std::vector<const NotificationEntry*>>; + using ClientStates = std::map<SchedulerClientType, const ClientState*>; + BackgroundTaskCoordinator( + std::unique_ptr<NotificationBackgroundTaskScheduler> background_task, + const SchedulerConfig* config, + base::Clock* clock); + virtual ~BackgroundTaskCoordinator(); + + // Schedule background task based on current notification in the storage. + virtual void ScheduleBackgroundTask(Notifications notifications, + ClientStates client_states, + SchedulerTaskTime task_start_time); + + private: + // The class that actually schedules platform background task. + std::unique_ptr<NotificationBackgroundTaskScheduler> background_task_; + + // System configuration. + const SchedulerConfig* config_; + + // Clock to query the current timestamp. + base::Clock* clock_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinator); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_BACKGROUND_TASK_COORDINATOR_H_
diff --git a/chrome/browser/notifications/scheduler/internal/background_task_coordinator_unittest.cc b/chrome/browser/notifications/scheduler/internal/background_task_coordinator_unittest.cc new file mode 100644 index 0000000..28d15a3d --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/background_task_coordinator_unittest.cc
@@ -0,0 +1,337 @@ +// Copyright 2019 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/notifications/scheduler/internal/background_task_coordinator.h" + +#include <map> +#include <memory> +#include <utility> +#include <vector> + +#include "base/test/scoped_task_environment.h" +#include "base/time/clock.h" +#include "chrome/browser/notifications/scheduler/internal/notification_entry.h" +#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h" +#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h" +#include "chrome/browser/notifications/scheduler/test/test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; + +namespace notifications { +namespace { + +using Notifications = BackgroundTaskCoordinator::Notifications; +using ClientStates = BackgroundTaskCoordinator::ClientStates; + +const char kGuid[] = "1234"; +const std::vector<test::ImpressionTestData> kSingleClientImpressionTestData = { + {SchedulerClientType::kTest1, + 1 /* current_max_daily_show */, + {}, + base::nullopt /* suppression_info */}}; + +const std::vector<test::ImpressionTestData> kClientsImpressionTestData = { + {SchedulerClientType::kTest1, + 1 /* current_max_daily_show */, + {}, + base::nullopt /* suppression_info */}, + {SchedulerClientType::kTest2, + 2 /* current_max_daily_show */, + {}, + base::nullopt /* suppression_info */}}; + +class MockNotificationBackgroundTaskScheduler + : public NotificationBackgroundTaskScheduler { + public: + MockNotificationBackgroundTaskScheduler() = default; + ~MockNotificationBackgroundTaskScheduler() override = default; + MOCK_METHOD3(Schedule, + void(notifications::SchedulerTaskTime, + base::TimeDelta, + base::TimeDelta)); + MOCK_METHOD0(Cancel, void()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockNotificationBackgroundTaskScheduler); +}; + +// Clock to mock Clock::Now() to get a fixed time in the test. +class FakeClock : public base::Clock { + public: + FakeClock() = default; + ~FakeClock() override = default; + + void SetTime(const base::Time& time) { time_ = time; } + + private: + // base::Clock implementation. + base::Time Now() const override { return time_; } + + base::Time time_; + DISALLOW_COPY_AND_ASSIGN(FakeClock); +}; + +struct TestData { + // Impression data as the input. + std::vector<test::ImpressionTestData> impression_test_data; + + // Notification entries as the input. + std::vector<NotificationEntry> notification_entries; + + // The type of current background task. + SchedulerTaskTime task_start_time = SchedulerTaskTime::kMorning; +}; + +class BackgroundTaskCoordinatorTest : public testing::Test { + public: + BackgroundTaskCoordinatorTest() = default; + ~BackgroundTaskCoordinatorTest() override = default; + + protected: + void SetUp() override { + // Setup configuration used by this test. + config_.morning_task_hour = 6; + config_.evening_task_hour = 18; + config_.max_daily_shown_all_type = 3; + config_.max_daily_shown_per_type = 2; + config_.suppression_duration = base::TimeDelta::FromDays(3); + + auto background_task = + std::make_unique<MockNotificationBackgroundTaskScheduler>(); + background_task_ = background_task.get(); + coordinator_ = std::make_unique<BackgroundTaskCoordinator>( + std::move(background_task), &config_, &clock_); + } + + MockNotificationBackgroundTaskScheduler* background_task() { + return background_task_; + } + + SchedulerConfig* config() { return &config_; } + + void SetNow(const char* now_str) { + base::Time now = GetTime(now_str); + clock_.SetTime(now); + } + + base::Time GetTime(const char* time_str) { + base::Time time; + bool success = base::Time::FromString(time_str, &time); + DCHECK(success); + return time; + } + + void ScheduleTask(const TestData& test_data) { + test_data_ = test_data; + test::AddImpressionTestData(test_data_.impression_test_data, + &client_states_); + std::map<SchedulerClientType, const ClientState*> client_states; + for (const auto& type : client_states_) { + client_states.emplace(type.first, type.second.get()); + } + + Notifications notifications; + for (const auto& entry : test_data_.notification_entries) { + notifications[entry.type].emplace_back(&entry); + } + coordinator_->ScheduleBackgroundTask(std::move(notifications), + std::move(client_states), + test_data_.task_start_time); + } + + private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + FakeClock clock_; + SchedulerConfig config_; + std::unique_ptr<BackgroundTaskCoordinator> coordinator_; + MockNotificationBackgroundTaskScheduler* background_task_; + TestData test_data_; + std::map<SchedulerClientType, std::unique_ptr<ClientState>> client_states_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinatorTest); +}; + +// No notification persisted, then no background task needs to be scheduled. +// And current task should be canceled. +TEST_F(BackgroundTaskCoordinatorTest, NoNotification) { + EXPECT_CALL(*background_task(), Cancel()); + EXPECT_CALL(*background_task(), Schedule(_, _, _)).Times(0); + TestData test_data; + test_data.impression_test_data = kSingleClientImpressionTestData; + ScheduleTask(test_data); +} + +// In a morning task, find one notification and schedule an evening task. +TEST_F(BackgroundTaskCoordinatorTest, InMorningScheduleEvening) { + const char kNow[] = "04/25/20 01:00:00 AM"; + SetNow(kNow); + EXPECT_CALL(*background_task(), Cancel()).Times(0); + // Expected to run task this evening. + auto expected_window_start = GetTime("04/25/20 18:00:00 PM") - GetTime(kNow); + EXPECT_CALL(*background_task(), + Schedule(_, expected_window_start, + expected_window_start + + config()->background_task_window_duration)); + + NotificationEntry entry(SchedulerClientType::kTest1, kGuid); + TestData test_data{ + kSingleClientImpressionTestData, {entry}, SchedulerTaskTime::kMorning}; + ScheduleTask(test_data); +} + +// In morning task, schedule evening task but throttled, schedule to next +// morning. +TEST_F(BackgroundTaskCoordinatorTest, InMorningScheduleEveningThrottled) { + const char kNow[] = "04/25/20 02:00:00 PM"; + SetNow(kNow); + + EXPECT_CALL(*background_task(), Cancel()).Times(0); + // Expected to run task next morning. + EXPECT_CALL(*background_task(), + Schedule(_, GetTime("04/26/20 06:00:00 AM") - GetTime(kNow), _)); + + auto impression_data = kSingleClientImpressionTestData; + Impression impression; + impression.create_time = GetTime("04/25/20 01:00:00 AM"); + impression_data.back().impressions.emplace_back(impression); + + NotificationEntry entry(SchedulerClientType::kTest1, kGuid); + TestData test_data{impression_data, {entry}, SchedulerTaskTime::kMorning}; + ScheduleTask(test_data); +} + +// In an evening task, schedule background task to run next morning. +TEST_F(BackgroundTaskCoordinatorTest, InEveningScheduleNextMorning) { + const char kNow[] = "04/25/20 18:00:00 PM"; + SetNow(kNow); + EXPECT_CALL(*background_task(), Cancel()).Times(0); + // Expected to run task next morning. + auto expected_window_start = GetTime("04/26/20 06:00:00 AM") - GetTime(kNow); + EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _)); + + NotificationEntry entry(SchedulerClientType::kTest1, kGuid); + TestData test_data{ + kSingleClientImpressionTestData, {entry}, SchedulerTaskTime::kEvening}; + ScheduleTask(test_data); +} + +// In an evening task, schedule background task to run next morning, even if we +// reached the daily max. +TEST_F(BackgroundTaskCoordinatorTest, InEveningScheduleNextMorningThrottled) { + const char kNow[] = "04/25/20 18:00:00 PM"; + SetNow(kNow); + EXPECT_CALL(*background_task(), Cancel()).Times(0); + // Expected to run task next morning. + auto expected_window_start = GetTime("04/26/20 06:00:00 AM") - GetTime(kNow); + EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _)); + + // We have reached daily max. + auto impression_data = kSingleClientImpressionTestData; + Impression impression; + impression.create_time = GetTime("04/25/20 01:00:00 AM"); + impression_data.back().impressions.emplace_back(impression); + + NotificationEntry entry(SchedulerClientType::kTest1, kGuid); + TestData test_data{ + kSingleClientImpressionTestData, {entry}, SchedulerTaskTime::kEvening}; + ScheduleTask(test_data); +} + +// Suppression will result in background task scheduled after suppression +// expired. +TEST_F(BackgroundTaskCoordinatorTest, Suppression) { + const char kNow[] = "04/25/20 06:00:00 AM"; + SetNow(kNow); + EXPECT_CALL(*background_task(), Cancel()).Times(0); + // Expected to run task in the morning after suppression expired. + auto expected_window_start = GetTime("04/28/20 06:00:00 AM") - GetTime(kNow); + EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _)); + + auto impression_data = kSingleClientImpressionTestData; + impression_data.back().suppression_info = SuppressionInfo( + GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(3)); + + NotificationEntry entry(SchedulerClientType::kTest1, kGuid); + TestData test_data{impression_data, {entry}, SchedulerTaskTime::kMorning}; + ScheduleTask(test_data); +} + +// If two different types want to schedule at different times, pick the earilier +// one. +TEST_F(BackgroundTaskCoordinatorTest, ScheduleEarlierTime) { + const char kNow[] = "04/25/20 01:00:00 AM"; + SetNow(kNow); + EXPECT_CALL(*background_task(), Cancel()).Times(0); + // kTest1 type will run this evening, kTest2 will run task 3 days later. + // Expected to run the earilier task. + auto expected_window_start = GetTime("04/25/20 18:00:00 PM") - GetTime(kNow); + EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _)); + + NotificationEntry entry1(SchedulerClientType::kTest1, kGuid); + NotificationEntry entry2(SchedulerClientType::kTest2, "guid_entry2"); + auto impression_data = kClientsImpressionTestData; + impression_data[0].suppression_info = SuppressionInfo( + GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(3)); + TestData test_data{ + impression_data, {entry1, entry2}, SchedulerTaskTime::kMorning}; + ScheduleTask(test_data); +} + +// If reached |max_daily_shown_all_type|, background task should run tomorrow. +TEST_F(BackgroundTaskCoordinatorTest, InMorningThrottledAllTypes) { + const char kNow[] = "04/25/20 05:00:00 AM"; + SetNow(kNow); + EXPECT_CALL(*background_task(), Cancel()).Times(0); + // Expected to run task next morning. + auto expected_window_start = GetTime("04/26/20 06:00:00 AM") - GetTime(kNow); + EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _)); + + auto impression_data = kClientsImpressionTestData; + Impression impression; + impression.create_time = GetTime("04/25/20 01:00:00 AM"); + + // Make sure we reach daily max for all types. + for (int i = 0; i < config()->max_daily_shown_all_type; i++) + impression_data.back().impressions.emplace_back(impression); + + NotificationEntry entry(SchedulerClientType::kTest1, kGuid); + TestData test_data{impression_data, {entry}, SchedulerTaskTime::kMorning}; + ScheduleTask(test_data); +} + +// If reached |max_daily_shown_all_type| and all types have suppression, +// background task should run after one suppression expired. +TEST_F(BackgroundTaskCoordinatorTest, ThrottledAllTypesAndSuppression) { + const char kNow[] = "04/25/20 05:00:00 AM"; + SetNow(kNow); + EXPECT_CALL(*background_task(), Cancel()).Times(0); + // Expected to run after 3 days suppression ends. + auto expected_window_start = GetTime("04/28/20 06:00:00 AM") - GetTime(kNow); + EXPECT_CALL(*background_task(), Schedule(_, expected_window_start, _)); + + auto impression_data = kClientsImpressionTestData; + Impression impression; + impression.create_time = GetTime("04/25/20 01:00:00 AM"); + + // Make sure we reach daily max for all types. + for (int i = 0; i < config()->max_daily_shown_all_type; i++) + impression_data[1].impressions.emplace_back(impression); + + // Suppression for both types. + impression_data[0].suppression_info = SuppressionInfo( + GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(3)); + impression_data[1].suppression_info = SuppressionInfo( + GetTime("04/25/20 00:00:00 AM"), base::TimeDelta::FromDays(4)); + + NotificationEntry entry1(SchedulerClientType::kTest1, "test_guid_1"); + NotificationEntry entry2(SchedulerClientType::kTest2, "test_guid_2"); + TestData test_data{ + impression_data, {entry1, entry2}, SchedulerTaskTime::kMorning}; + ScheduleTask(test_data); +} + +} // namespace +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/collection_store.h b/chrome/browser/notifications/scheduler/internal/collection_store.h new file mode 100644 index 0000000..7a1fdf0 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/collection_store.h
@@ -0,0 +1,53 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_COLLECTION_STORE_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_COLLECTION_STORE_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" + +namespace notifications { + +// A storage interface which loads a collection of data type T into memory +// during initialization. When updating the data, T will be copied to the actual +// storage layer since the caller will keep in memory data as well. +template <typename T> +class CollectionStore { + public: + using Entries = std::vector<std::unique_ptr<T>>; + using LoadCallback = base::OnceCallback<void(bool, Entries)>; + using InitCallback = base::OnceCallback<void(bool)>; + using UpdateCallback = base::OnceCallback<void(bool)>; + + // Initializes the database and loads all entries into memory. + virtual void InitAndLoad(LoadCallback callback) = 0; + + // Adds an entry to the storage. + virtual void Add(const std::string& key, + const T& entry, + UpdateCallback callback) = 0; + + // Updates an entry. + virtual void Update(const std::string& key, + const T& entry, + UpdateCallback callback) = 0; + + // Deletes an entry from storage. + virtual void Delete(const std::string& key, UpdateCallback callback) = 0; + + CollectionStore() = default; + virtual ~CollectionStore() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(CollectionStore); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_COLLECTION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/internal/display_decider.cc b/chrome/browser/notifications/scheduler/internal/display_decider.cc new file mode 100644 index 0000000..6e7a8e0 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/display_decider.cc
@@ -0,0 +1,190 @@ +// Copyright 2019 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/notifications/scheduler/internal/display_decider.h" + +#include <algorithm> + +#include "chrome/browser/notifications/scheduler/internal/distribution_policy.h" +#include "chrome/browser/notifications/scheduler/internal/impression_types.h" +#include "chrome/browser/notifications/scheduler/internal/notification_entry.h" +#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h" +#include "chrome/browser/notifications/scheduler/internal/scheduler_utils.h" + +using Notifications = notifications::DisplayDecider::Notifications; +using Results = notifications::DisplayDecider::Results; +using ClientStates = notifications::DisplayDecider::ClientStates; + +namespace notifications { +namespace { + +// Helper class contains the actual logic to decide which notifications to show. +// This is an one-shot class, callers should create a new object each time. +class DecisionHelper { + public: + DecisionHelper(const SchedulerConfig* config, + const std::vector<SchedulerClientType>& clients, + std::unique_ptr<DistributionPolicy> distribution_policy, + SchedulerTaskTime task_start_time, + Notifications notifications, + ClientStates client_states) + : notifications_(std::move(notifications)), + current_task_start_time_(task_start_time), + client_states_(std::move(client_states)), + config_(config), + clients_(clients), + policy_(std::move(distribution_policy)), + daily_max_to_show_all_types_(0), + last_shown_type_(SchedulerClientType::kUnknown), + shown_(0) {} + + ~DecisionHelper() = default; + + // Figures out a list of notifications to show. + void DecideNotificationToShow(Results* results) { + ComputeDailyQuotaAllTypes(); + CountNotificationsShownToday(); + PickNotificationToShow(results); + } + + private: + void ComputeDailyQuotaAllTypes() { + // Only background task launch needs to comply to |policy_|. + if (current_task_start_time_ == SchedulerTaskTime::kUnknown) { + daily_max_to_show_all_types_ = config_->max_daily_shown_all_type; + return; + } + + int quota = std::max(config_->max_daily_shown_all_type - shown_, 0); + DCHECK(policy_); + daily_max_to_show_all_types_ = + std::min(config_->max_daily_shown_all_type, + policy_->MaxToShow(current_task_start_time_, quota)); + } + + void CountNotificationsShownToday() { + for (const auto& pair : client_states_) { + auto type = pair.first; + // TODO(xingliu): Ensure deprecated clients will not have data in storage. + DCHECK(std::find(clients_.begin(), clients_.end(), type) != + clients_.end()); + } + + NotificationsShownToday(client_states_, &shown_per_type_, &shown_, + &last_shown_type_); + } + + // Picks a list of notifications to show. + void PickNotificationToShow(Results* to_show) { + DCHECK(to_show); + if (shown_ > config_->max_daily_shown_all_type || clients_.empty()) + return; + + // No previous shown notification, move the iterator to last element. + // We will iterate through all client types later. + auto it = std::find(clients_.begin(), clients_.end(), last_shown_type_); + if (it == clients_.end()) { + DCHECK_EQ(last_shown_type_, SchedulerClientType::kUnknown); + last_shown_type_ = clients_.back(); + it = clients_.end() - 1; + } + + DCHECK_NE(last_shown_type_, SchedulerClientType::kUnknown); + size_t steps = 0u; + + // Circling around all clients to find new notification to show. + // TODO(xingliu): Apply scheduling parameters here. + do { + // Move the iterator to next client type. + DCHECK(it != clients_.end()); + if (++it == clients_.end()) + it = clients_.begin(); + ++steps; + + SchedulerClientType type = *it; + + // Check quota for all types and current background task type. + if (ReachDailyQuota()) + break; + + // Check quota for this type, and continue to iterate other types. + if (NoMoreNotificationToShow(type)) + continue; + + // Show the last notification in the vector. Notice the order depends on + // how the vector is sorted. + to_show->emplace(notifications_[type].back()->guid); + notifications_[type].pop_back(); + shown_per_type_[type]++; + shown_++; + steps = 0u; + + // Stop if we didn't find anything new to show, and have looped around + // all clients. + } while (steps <= clients_.size()); + } + + bool NoMoreNotificationToShow(SchedulerClientType type) { + auto it = client_states_.find(type); + int max_daily_show = + it == client_states_.end() ? 0 : it->second->current_max_daily_show; + + return notifications_[type].empty() || + shown_per_type_[type] >= config_->max_daily_shown_per_type || + shown_per_type_[type] >= max_daily_show; + } + + bool ReachDailyQuota() const { + return shown_ >= daily_max_to_show_all_types_; + } + + // Scheduled notifications as candidates to display to the user. + Notifications notifications_; + + const SchedulerTaskTime current_task_start_time_; + const ClientStates client_states_; + const SchedulerConfig* config_; + const std::vector<SchedulerClientType> clients_; + std::unique_ptr<DistributionPolicy> policy_; + int daily_max_to_show_all_types_; + + SchedulerClientType last_shown_type_; + std::map<SchedulerClientType, int> shown_per_type_; + int shown_; + + DISALLOW_COPY_AND_ASSIGN(DecisionHelper); +}; + +class DisplayDeciderImpl : public DisplayDecider { + public: + DisplayDeciderImpl() = default; + ~DisplayDeciderImpl() override = default; + + private: + // DisplayDecider implementation. + void FindNotificationsToShow( + const SchedulerConfig* config, + std::vector<SchedulerClientType> clients, + std::unique_ptr<DistributionPolicy> distribution_policy, + SchedulerTaskTime task_start_time, + Notifications notifications, + ClientStates client_states, + Results* results) override { + auto helper = std::make_unique<DecisionHelper>( + config, std::move(clients), std::move(distribution_policy), + task_start_time, std::move(notifications), std::move(client_states)); + helper->DecideNotificationToShow(results); + } + + DISALLOW_COPY_AND_ASSIGN(DisplayDeciderImpl); +}; + +} // namespace + +// static +std::unique_ptr<DisplayDecider> DisplayDecider::Create() { + return std::make_unique<DisplayDeciderImpl>(); +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/display_decider.h b/chrome/browser/notifications/scheduler/internal/display_decider.h new file mode 100644 index 0000000..cacb5df1 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/display_decider.h
@@ -0,0 +1,58 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_DISPLAY_DECIDER_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_DISPLAY_DECIDER_H_ + +#include <map> +#include <memory> +#include <set> +#include <utility> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" + +namespace notifications { + +class DistributionPolicy; +struct ClientState; +struct NotificationEntry; +struct SchedulerConfig; + +// This class uses scheduled notifications data and notification impression data +// of each notification type to find a list of notification that should be +// displayed to the user. +// All operations should be done on the main thread. +class DisplayDecider { + public: + using Notifications = + std::map<SchedulerClientType, std::vector<const NotificationEntry*>>; + using ClientStates = std::map<SchedulerClientType, const ClientState*>; + using Results = std::set<std::string>; + + // Creates the decider to determine notifications to show. + static std::unique_ptr<DisplayDecider> Create(); + + DisplayDecider() = default; + virtual ~DisplayDecider() = default; + + // Finds notifications to show. Returns a list of notification guids. + virtual void FindNotificationsToShow( + const SchedulerConfig* config, + std::vector<SchedulerClientType> clients, + std::unique_ptr<DistributionPolicy> distribution_policy, + SchedulerTaskTime task_start_time, + Notifications notifications, + ClientStates client_states, + Results* results) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(DisplayDecider); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_DISPLAY_DECIDER_H_
diff --git a/chrome/browser/notifications/scheduler/internal/display_decider_unittest.cc b/chrome/browser/notifications/scheduler/internal/display_decider_unittest.cc new file mode 100644 index 0000000..da5bc05 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/display_decider_unittest.cc
@@ -0,0 +1,151 @@ +// Copyright 2019 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/notifications/scheduler/internal/display_decider.h" + +#include <map> +#include <memory> +#include <vector> + +#include "base/strings/stringprintf.h" +#include "base/test/scoped_task_environment.h" +#include "chrome/browser/notifications/scheduler/internal/distribution_policy.h" +#include "chrome/browser/notifications/scheduler/internal/notification_entry.h" +#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" +#include "chrome/browser/notifications/scheduler/test/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace notifications { +namespace { + +// Default suppression info used in this test. +const SuppressionInfo kSuppressionInfo = + SuppressionInfo(base::Time::Now(), base::TimeDelta::FromDays(56)); + +// Initial state for test cases with a single registered client. +const std::vector<test::ImpressionTestData> kSingleClientImpressionTestData = { + {SchedulerClientType::kTest1, + 2 /* current_max_daily_show */, + {}, + base::nullopt /* suppression_info */}}; + +const std::vector<test::ImpressionTestData> kClientsImpressionTestData = { + {SchedulerClientType::kTest1, + 2 /* current_max_daily_show */, + {}, + base::nullopt /* suppression_info */}, + {SchedulerClientType::kTest2, + 5 /* current_max_daily_show */, + {}, + base::nullopt /* suppression_info */}, + {SchedulerClientType::kTest3, + 0 /* current_max_daily_show */, + {}, + kSuppressionInfo}}; + +struct TestData { + // Impression data as the input. + std::vector<test::ImpressionTestData> impression_test_data; + + // Notification entries as the input. + std::vector<NotificationEntry> notification_entries; + + // The type of current background task. + SchedulerTaskTime task_start_time = SchedulerTaskTime::kUnknown; + + // Expected output data. + DisplayDecider::Results expected; +}; + +std::string DebugString(const DisplayDecider::Results& results) { + std::string debug_string("notifications_to_show: \n"); + for (const auto& guid : results) + debug_string += base::StringPrintf("%s ", guid.c_str()); + + return debug_string; +} + +// TODO(xingliu): Add more test cases. +class DisplayDeciderTest : public testing::Test { + public: + DisplayDeciderTest() = default; + ~DisplayDeciderTest() override = default; + + void SetUp() override { + // Setup configuration used by this test. + config_.morning_task_hour = 7; + config_.evening_task_hour = 18; + config_.max_daily_shown_all_type = 3; + } + + protected: + // Initializes a test case with input data. + void RunTestCase(const TestData& test_data) { + test_data_ = test_data; + test::AddImpressionTestData(test_data_.impression_test_data, + &client_states_); + + DisplayDecider::Notifications notifications; + for (const auto& entry : test_data_.notification_entries) { + notifications[entry.type].emplace_back(&entry); + } + std::vector<SchedulerClientType> clients; + + std::map<SchedulerClientType, const ClientState*> client_states; + for (const auto& type : client_states_) { + client_states.emplace(type.first, type.second.get()); + clients.emplace_back(type.first); + } + + // Copy test inputs into |decider_|. + decider_ = DisplayDecider::Create(); + decider_->FindNotificationsToShow( + &config_, std::move(clients), DistributionPolicy::Create(), + test_data_.task_start_time, std::move(notifications), + std::move(client_states), &results_); + + // Verify output. + EXPECT_EQ(results_, test_data_.expected) + << "Actual result: \n" + << DebugString(results_) << " \n Expected result: \n" + << DebugString(test_data_.expected); + } + + private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + + TestData test_data_; + SchedulerConfig config_; + + std::map<SchedulerClientType, std::unique_ptr<ClientState>> client_states_; + + // Test target class and output. + std::unique_ptr<DisplayDecider> decider_; + DisplayDecider::Results results_; + + DISALLOW_COPY_AND_ASSIGN(DisplayDeciderTest); +}; + +TEST_F(DisplayDeciderTest, NoNotification) { + TestData data{kClientsImpressionTestData, + {}, + SchedulerTaskTime::kEvening, + DisplayDecider::Results()}; + RunTestCase(data); +} + +// Simple test case to verify new notifcaiton can be selected to show. +TEST_F(DisplayDeciderTest, PickNewMorning) { + NotificationEntry entry(SchedulerClientType::kTest1, "guid123"); + DisplayDecider::Results expected = {"guid123"}; + TestData data{kSingleClientImpressionTestData, + {entry}, + SchedulerTaskTime::kMorning, + std::move(expected)}; + RunTestCase(data); +} + +} // namespace +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/distribution_policy.cc b/chrome/browser/notifications/scheduler/internal/distribution_policy.cc new file mode 100644 index 0000000..7319635 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/distribution_policy.cc
@@ -0,0 +1,46 @@ +// Copyright 2019 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/notifications/scheduler/internal/distribution_policy.h" + +#include "base/logging.h" + +namespace notifications { +namespace { + +// Evenly distributes notifications to show in morning and evening. Morning +// will have one more to show if the total quota is odd. +class EvenDistributionHigherMorning : public DistributionPolicy { + public: + EvenDistributionHigherMorning() = default; + ~EvenDistributionHigherMorning() override = default; + + private: + // DistributionPolicy implementation. + int MaxToShow(SchedulerTaskTime task_start_time, int quota) override { + DCHECK_GE(quota, 0); + switch (task_start_time) { + case SchedulerTaskTime::kUnknown: + NOTREACHED(); + return quota; + case SchedulerTaskTime::kMorning: + return quota / 2 + quota % 2; + case SchedulerTaskTime::kEvening: + // The task running in the evening should flush all the remaining + // notifications. + return quota; + } + } + + DISALLOW_COPY_AND_ASSIGN(EvenDistributionHigherMorning); +}; + +} // namespace + +// static +std::unique_ptr<DistributionPolicy> DistributionPolicy::Create() { + return std::make_unique<EvenDistributionHigherMorning>(); +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/distribution_policy.h b/chrome/browser/notifications/scheduler/internal/distribution_policy.h new file mode 100644 index 0000000..ac1a38bf --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/distribution_policy.h
@@ -0,0 +1,36 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_DISTRIBUTION_POLICY_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_DISTRIBUTION_POLICY_H_ + +#include <memory> + +#include "base/macros.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" + +namespace notifications { + +// Defines how to distribute notifications to show in different background tasks +// in a day. +class DistributionPolicy { + public: + // Creates the default distribution policy. + static std::unique_ptr<DistributionPolicy> Create(); + + DistributionPolicy() = default; + virtual ~DistributionPolicy() = default; + + // Returns the maximum number of notifications to show in the background task + // starts at |task_start_time|. Suppose we at most can show |quota| number of + // new notifications during the current background task. + virtual int MaxToShow(SchedulerTaskTime task_start_time, int quota) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(DistributionPolicy); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_DISTRIBUTION_POLICY_H_
diff --git a/chrome/browser/notifications/scheduler/internal/distribution_policy_unittest.cc b/chrome/browser/notifications/scheduler/internal/distribution_policy_unittest.cc new file mode 100644 index 0000000..0606e61b --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/distribution_policy_unittest.cc
@@ -0,0 +1,42 @@ +// Copyright 2019 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/notifications/scheduler/internal/distribution_policy.h" + +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace notifications { +namespace { + +int MaxToShow(DistributionPolicy* policy, + SchedulerTaskTime task_start_time, + int quota) { + DCHECK(policy); + return policy->MaxToShow(task_start_time, quota); +} + +TEST(DistributionPolicyTest, EvenDistributionHigherMorning) { + auto policy = DistributionPolicy::Create(); + + EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 5 /* quota */), + 3); + EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 4 /* quota */), + 2); + + // Evening task should flush all remaining quota. + EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kEvening, 5 /* quota */), + 5); + EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kEvening, 4 /* quota */), + 4); + + // Test 0 quota. + EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 0 /* quota */), + 0); + EXPECT_EQ(MaxToShow(policy.get(), SchedulerTaskTime::kMorning, 0 /* quota */), + 0); +} + +} // namespace +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/icon_entry.cc b/chrome/browser/notifications/scheduler/internal/icon_entry.cc new file mode 100644 index 0000000..d21f17c1 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/icon_entry.cc
@@ -0,0 +1,16 @@ +// Copyright 2019 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/notifications/scheduler/internal/icon_entry.h" + +namespace notifications { + +IconEntry::IconEntry() = default; + +IconEntry::IconEntry(IconEntry&& other) { + uuid.swap(other.uuid); + data.swap(other.data); +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/icon_entry.h b/chrome/browser/notifications/scheduler/internal/icon_entry.h new file mode 100644 index 0000000..c941f04 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/icon_entry.h
@@ -0,0 +1,38 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_ICON_ENTRY_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_ICON_ENTRY_H_ + +#include <string> +#include <utility> + +#include "base/macros.h" + +namespace notifications { + +// The database entry that contains a notification icon, deserialized from the +// icon protobuffer. +// The icon can be a large chunk of memory so should be used in caution. The +// format of the data is the same as the format in the protobuffer, and may need +// to be converted to bitmap when used by the UI. +struct IconEntry { + using IconData = std::string; + + IconEntry(); + IconEntry(IconEntry&& other); + + // Unique identifier for the icon database entry. + std::string uuid; + + // Raw data of the icon. + IconData data; + + private: + DISALLOW_COPY_AND_ASSIGN(IconEntry); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_ICON_ENTRY_H_
diff --git a/chrome/browser/notifications/scheduler/internal/icon_store.cc b/chrome/browser/notifications/scheduler/internal/icon_store.cc new file mode 100644 index 0000000..9652056a --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/icon_store.cc
@@ -0,0 +1,89 @@ +// Copyright 2019 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/notifications/scheduler/internal/icon_store.h" + +#include <utility> + +#include "chrome/browser/notifications/scheduler/internal/icon_entry.h" +#include "chrome/browser/notifications/scheduler/internal/proto_conversion.h" + +namespace leveldb_proto { + +void DataToProto(notifications::IconEntry* icon_entry, + notifications::proto::Icon* proto) { + IconEntryToProto(icon_entry, proto); +} + +void ProtoToData(notifications::proto::Icon* proto, + notifications::IconEntry* icon_entry) { + IconEntryFromProto(proto, icon_entry); +} + +} // namespace leveldb_proto + +namespace notifications { + +using KeyEntryPair = std::pair<std::string, IconEntry>; +using KeyEntryVector = std::vector<KeyEntryPair>; +using KeyVector = std::vector<std::string>; + +IconProtoDbStore::IconProtoDbStore( + std::unique_ptr<leveldb_proto::ProtoDatabase<proto::Icon, IconEntry>> db) + : db_(std::move(db)), weak_ptr_factory_(this) {} + +IconProtoDbStore::~IconProtoDbStore() = default; + +void IconProtoDbStore::Init(InitCallback callback) { + db_->Init(base::BindOnce(&IconProtoDbStore::OnDbInitialized, + weak_ptr_factory_.GetWeakPtr(), + std::move(callback))); +} + +void IconProtoDbStore::Load(const std::string& key, LoadCallback callback) { + db_->GetEntry( + key, base::BindOnce(&IconProtoDbStore::OnIconEntryLoaded, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} + +void IconProtoDbStore::Add(const std::string& key, + std::unique_ptr<IconEntry> entry, + UpdateCallback callback) { + auto entries_to_save = std::make_unique<KeyEntryVector>(); + // TODO(xingliu): See if proto database can use + // std::vector<std::unique_ptr<T>> to avoid copy T into std::pair. Currently + // some code uses the copy constructor that force T to be copyable. + entries_to_save->emplace_back( + std::make_pair(key, std::move(*entry.release()))); + db_->UpdateEntries(std::move(entries_to_save), std::make_unique<KeyVector>(), + std::move(callback)); +} + +void IconProtoDbStore::Delete(const std::string& key, UpdateCallback callback) { + auto keys_to_delete = std::make_unique<KeyVector>(); + keys_to_delete->emplace_back(key); + db_->UpdateEntries(std::make_unique<KeyEntryVector>(), + std::move(keys_to_delete), std::move(callback)); +} + +void IconProtoDbStore::OnDbInitialized( + InitCallback callback, + leveldb_proto::Enums::InitStatus status) { + bool success = (status == leveldb_proto::Enums::InitStatus::kOK); + std::move(callback).Run(success); +} + +void IconProtoDbStore::OnIconEntryLoaded( + LoadCallback callback, + bool success, + std::unique_ptr<IconEntry> icon_entry) { + if (!success) { + std::move(callback).Run(false, nullptr); + return; + } + + std::move(callback).Run(true, std::move(icon_entry)); +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/icon_store.h b/chrome/browser/notifications/scheduler/internal/icon_store.h new file mode 100644 index 0000000..b016b79 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/icon_store.h
@@ -0,0 +1,95 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_ICON_STORE_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_ICON_STORE_H_ + +#include <memory> +#include <string> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/notifications/proto/icon.pb.h" +#include "chrome/browser/notifications/scheduler/internal/icon_entry.h" +#include "components/leveldb_proto/public/proto_database.h" + +// Forward declaration for proto conversion. +namespace leveldb_proto { +void DataToProto(notifications::IconEntry* icon_entry, + notifications::proto::Icon* proto); + +void ProtoToData(notifications::proto::Icon* proto, + notifications::IconEntry* icon_entry); +} // namespace leveldb_proto + +namespace notifications { + +// Storage interface used to read/write icon data, each time only one icon can +// be loaded into memory. +class IconStore { + public: + using LoadCallback = + base::OnceCallback<void(bool, std::unique_ptr<IconEntry>)>; + using InitCallback = base::OnceCallback<void(bool)>; + using UpdateCallback = base::OnceCallback<void(bool)>; + + // Initializes the storage. + virtual void Init(InitCallback callback) = 0; + + // Loads one icon. + virtual void Load(const std::string& key, LoadCallback callback) = 0; + + // Adds one icon to storage. + virtual void Add(const std::string& key, + std::unique_ptr<IconEntry> entry, + UpdateCallback callback) = 0; + + // Deletes an icon. + virtual void Delete(const std::string& key, UpdateCallback callback) = 0; + + IconStore() = default; + virtual ~IconStore() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(IconStore); +}; + +// IconStore implementation backed by a proto database. +class IconProtoDbStore : public IconStore { + public: + explicit IconProtoDbStore( + std::unique_ptr<leveldb_proto::ProtoDatabase<proto::Icon, IconEntry>> db); + ~IconProtoDbStore() override; + + private: + // IconStore implementation. + void Init(InitCallback callback) override; + void Load(const std::string& key, LoadCallback callback) override; + void Add(const std::string& key, + std::unique_ptr<IconEntry> entry, + UpdateCallback callback) override; + void Delete(const std::string& key, UpdateCallback callback) override; + + // Called when the proto database is initialized. + void OnDbInitialized(InitCallback callback, + leveldb_proto::Enums::InitStatus status); + + // Called when the icon is retrieved from the database. + void OnIconEntryLoaded(LoadCallback callback, + bool success, + std::unique_ptr<IconEntry> icon_entry); + + // The proto database instance that persists data. + std::unique_ptr<leveldb_proto::ProtoDatabase<proto::Icon, IconEntry>> db_; + + base::WeakPtrFactory<IconProtoDbStore> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(IconProtoDbStore); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_ICON_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/internal/icon_store_unittest.cc b/chrome/browser/notifications/scheduler/internal/icon_store_unittest.cc new file mode 100644 index 0000000..3bb4ef3c --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/icon_store_unittest.cc
@@ -0,0 +1,170 @@ +// Copyright 2019 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/notifications/scheduler/internal/icon_store.h" + +#include <map> +#include <memory> +#include <string> +#include <utility> + +#include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" +#include "chrome/browser/notifications/proto/icon.pb.h" +#include "chrome/browser/notifications/scheduler/internal/icon_entry.h" +#include "components/leveldb_proto/testing/fake_db.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace notifications { +namespace { + +const char kEntryKey[] = "guid_key1"; +const char kEntryKey2[] = "guid_key2"; +const char kEntryId[] = "proto_id_1"; +const char kEntryId2[] = "proto_id_2"; +const char kEntryData[] = "data_1"; +const char kEntryData2[] = "data_2"; + +class IconStoreTest : public testing::Test { + public: + IconStoreTest() : load_result_(false), db_(nullptr) {} + ~IconStoreTest() override = default; + + void SetUp() override { + IconEntry entry; + entry.uuid = kEntryId; + entry.data = kEntryData; + proto::Icon proto; + leveldb_proto::DataToProto(&entry, &proto); + db_entries_.emplace(kEntryKey, proto); + + auto db = + std::make_unique<leveldb_proto::test::FakeDB<proto::Icon, IconEntry>>( + &db_entries_); + db_ = db.get(); + store_ = std::make_unique<IconProtoDbStore>(std::move(db)); + } + + void InitDb() { + store()->Init(base::DoNothing()); + db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK); + } + + void Load(const std::string& key) { + store()->Load(key, base::BindOnce(&IconStoreTest::OnEntryLoaded, + base::Unretained(this))); + } + + void OnEntryLoaded(bool success, std::unique_ptr<IconEntry> entry) { + loaded_entry_ = std::move(entry); + load_result_ = success; + } + + protected: + IconStore* store() { return store_.get(); } + leveldb_proto::test::FakeDB<proto::Icon, IconEntry>* db() { return db_; } + bool load_result() const { return load_result_; } + IconEntry* loaded_entry() { return loaded_entry_.get(); } + + void VerifyEntry(const std::string& uuid, const std::string& data) { + DCHECK(loaded_entry_); + EXPECT_EQ(loaded_entry_->uuid, uuid); + EXPECT_EQ(loaded_entry_->data, data); + } + + private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + std::unique_ptr<IconStore> store_; + std::map<std::string, proto::Icon> db_entries_; + std::unique_ptr<IconEntry> loaded_entry_; + bool load_result_; + leveldb_proto::test::FakeDB<proto::Icon, IconEntry>* db_; + + DISALLOW_COPY_AND_ASSIGN(IconStoreTest); +}; + +TEST_F(IconStoreTest, Init) { + store()->Init(base::BindOnce([](bool success) { EXPECT_TRUE(success); })); + db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK); + base::RunLoop().RunUntilIdle(); +} + +TEST_F(IconStoreTest, InitFailed) { + store()->Init(base::BindOnce([](bool success) { EXPECT_FALSE(success); })); + db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kCorrupt); + base::RunLoop().RunUntilIdle(); +} + +TEST_F(IconStoreTest, LoadOne) { + InitDb(); + Load(kEntryKey); + db()->GetCallback(true); + + // Verify data is loaded. + DCHECK(loaded_entry()); + EXPECT_TRUE(load_result()); + VerifyEntry(kEntryId, kEntryData); +} + +TEST_F(IconStoreTest, LoadFailed) { + InitDb(); + Load(kEntryKey); + db()->GetCallback(false); + + // Verify load failure. + EXPECT_FALSE(loaded_entry()); + EXPECT_FALSE(load_result()); +} + +TEST_F(IconStoreTest, Add) { + InitDb(); + + auto new_entry = std::make_unique<IconEntry>(); + new_entry->uuid = kEntryId2; + new_entry->data = kEntryData; + + store()->Add(kEntryKey2, std::move(new_entry), base::DoNothing()); + db()->UpdateCallback(true); + + // Verify the entry is added. + Load(kEntryKey2); + db()->GetCallback(true); + EXPECT_TRUE(load_result()); + VerifyEntry(kEntryId2, kEntryData); +} + +TEST_F(IconStoreTest, AddDuplicate) { + InitDb(); + + auto new_entry = std::make_unique<IconEntry>(); + new_entry->uuid = kEntryId; + new_entry->data = kEntryData2; + + store()->Add(kEntryKey, std::move(new_entry), base::DoNothing()); + db()->UpdateCallback(true); + + // Add a duplicate id is currently allowed, we just update the entry. + Load(kEntryKey); + db()->GetCallback(true); + EXPECT_TRUE(load_result()); + VerifyEntry(kEntryId, kEntryData2); +} + +TEST_F(IconStoreTest, Delete) { + InitDb(); + + // Delete the only entry. + store()->Delete(kEntryKey, + base::BindOnce([](bool success) { EXPECT_TRUE(success); })); + db()->UpdateCallback(true); + + // No entry can be loaded, move nullptr as result. + Load(kEntryKey); + db()->GetCallback(true); + EXPECT_FALSE(loaded_entry()); + EXPECT_TRUE(load_result()); +} + +} // namespace +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc new file mode 100644 index 0000000..7846da9c --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.cc
@@ -0,0 +1,392 @@ +// Copyright 2019 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/notifications/scheduler/internal/impression_history_tracker.h" + +#include <algorithm> +#include <utility> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/numerics/ranges.h" + +namespace notifications { + +// Comparator used to sort notification entries based on creation time. +bool CreateTimeCompare(const Impression& lhs, const Impression& rhs) { + return lhs.create_time < rhs.create_time; +} + +std::string ToDatabaseKey(SchedulerClientType type) { + switch (type) { + case SchedulerClientType::kTest1: + return "Test1"; + case SchedulerClientType::kTest2: + return "Test2"; + case SchedulerClientType::kTest3: + return "Test3"; + case SchedulerClientType::kUnknown: + NOTREACHED(); + return std::string(); + } +} + +ImpressionHistoryTrackerImpl::ImpressionHistoryTrackerImpl( + const SchedulerConfig& config, + std::unique_ptr<CollectionStore<ClientState>> store) + : store_(std::move(store)), + config_(config), + initialized_(false), + delegate_(nullptr), + weak_ptr_factory_(this) {} + +ImpressionHistoryTrackerImpl::~ImpressionHistoryTrackerImpl() = default; + +void ImpressionHistoryTrackerImpl::Init(Delegate* delegate, + InitCallback callback) { + DCHECK(!delegate_ && delegate); + delegate_ = delegate; + store_->InitAndLoad( + base::BindOnce(&ImpressionHistoryTrackerImpl::OnStoreInitialized, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} + +void ImpressionHistoryTrackerImpl::AnalyzeImpressionHistory() { + for (auto& client_state : client_states_) + AnalyzeImpressionHistory(client_state.second.get()); + if (MaybeUpdateAllDb()) + NotifyImpressionUpdate(); +} + +void ImpressionHistoryTrackerImpl::GetClientStates( + std::map<SchedulerClientType, const ClientState*>* client_states) const { + DCHECK(client_states); + client_states->clear(); + for (const auto& pair : client_states_) { + client_states->emplace(pair.first, pair.second.get()); + } +} + +void ImpressionHistoryTrackerImpl::OnClick(const std::string& notification_id) { + OnClickInternal(notification_id, true /*update_db*/); +} + +void ImpressionHistoryTrackerImpl::OnActionClick( + const std::string& notification_id, + ActionButtonType button_type) { + OnButtonClickInternal(notification_id, button_type, true /*update_db*/); +} + +void ImpressionHistoryTrackerImpl::OnDismiss( + const std::string& notification_id) { + OnDismissInternal(notification_id, true /*update_db*/); +} + +void ImpressionHistoryTrackerImpl::OnStoreInitialized( + InitCallback callback, + bool success, + CollectionStore<ClientState>::Entries entries) { + if (!success) { + std::move(callback).Run(false); + return; + } + + initialized_ = true; + + // Load the data to memory, and sort the impression list. + // TODO(xingliu): Persist ClientState for new registered client and remove + // deprecated client. https://crbug.com/968606. + for (auto it = entries.begin(); it != entries.end(); ++it) { + auto& entry = (*it); + auto type = entry->type; + std::sort(entry->impressions.begin(), entry->impressions.end(), + &CreateTimeCompare); + for (auto& impression : entry->impressions) { + impression_map_.emplace(impression.guid, &impression); + } + client_states_.emplace(type, std::move(*it)); + } + + std::move(callback).Run(true); +} + +void ImpressionHistoryTrackerImpl::AnalyzeImpressionHistory( + ClientState* client_state) { + DCHECK(client_state); + std::deque<Impression*> dismisses; + base::Time now = base::Time::Now(); + + for (auto it = client_state->impressions.begin(); + it != client_state->impressions.end();) { + auto* impression = &*it; + + // Prune out expired impression. + if (now - impression->create_time > config_.impression_expiration) { + impression_map_.erase(impression->guid); + client_state->impressions.erase(it++); + SetNeedsUpdate(client_state->type, true); + continue; + } else { + ++it; + } + + // TODO(xingliu): Use scheduling params to determine ImpressionResult. + // https://crbug.com/968880 + switch (impression->feedback) { + case UserFeedback::kDismiss: + dismisses.emplace_back(impression); + PruneImpressionByCreateTime( + &dismisses, impression->create_time - config_.dismiss_duration); + + // Three consecutive dismisses will result in suppression. + ApplyNegativeImpressions(client_state, &dismisses, + config_.dismiss_count); + break; + case UserFeedback::kClick: + OnClickInternal(impression->guid, false /*update_db*/); + break; + case UserFeedback::kHelpful: + OnButtonClickInternal(impression->guid, ActionButtonType::kHelpful, + false /*update_db*/); + break; + case UserFeedback::kNotHelpful: + OnButtonClickInternal(impression->guid, ActionButtonType::kUnhelpful, + false /*update_db*/); + break; + case UserFeedback::kIgnore: + break; + case UserFeedback::kNoFeedback: + FALLTHROUGH; + default: + // The user didn't interact with the notification yet. + continue; + break; + } + } + + // Check suppression expiration. + CheckSuppressionExpiration(client_state); +} + +// static +void ImpressionHistoryTrackerImpl::PruneImpressionByCreateTime( + std::deque<Impression*>* impressions, + const base::Time& start_time) { + DCHECK(impressions); + while (!impressions->empty()) { + if (impressions->front()->create_time > start_time) + break; + // Anything created before |start_time| is considered to have no effect and + // will never be processed again. + impressions->front()->integrated = true; + impressions->pop_front(); + } +} + +void ImpressionHistoryTrackerImpl::ApplyPositiveImpression( + ClientState* client_state, + Impression* impression) { + DCHECK(impression); + if (impression->integrated) + return; + + SetNeedsUpdate(client_state->type, true); + impression->integrated = true; + impression->impression = ImpressionResult::kPositive; + + // A positive impression directly releases the suppression. + if (client_state->suppression_info.has_value()) { + client_state->current_max_daily_show = + client_state->suppression_info->recover_goal; + client_state->suppression_info.reset(); + return; + } + + // Increase |current_max_daily_show| by 1. + client_state->current_max_daily_show = + base::ClampToRange(++client_state->current_max_daily_show, 0, + config_.max_daily_shown_per_type); +} + +void ImpressionHistoryTrackerImpl::ApplyNegativeImpressions( + ClientState* client_state, + std::deque<Impression*>* impressions, + size_t num_actions) { + if (impressions->size() < num_actions) + return; + + // Suppress the notification if the user performed consecutive operations that + // generates negative impressions. + for (size_t i = 0, size = impressions->size(); i < size; ++i) { + if ((*impressions)[i]->integrated) + continue; + + (*impressions)[i]->integrated = true; + + // Each user feedback after |num_action| will apply a new negative + // impression. + if (i + 1 >= num_actions) + ApplyNegativeImpression(client_state, (*impressions)[i]); + } +} + +void ImpressionHistoryTrackerImpl::ApplyNegativeImpression( + ClientState* client_state, + Impression* impression) { + if (impression->integrated) + return; + + SetNeedsUpdate(client_state->type, true); + impression->integrated = true; + impression->impression = ImpressionResult::kNegative; + + // Suppress the notification, the user will not see this type of notification + // for a while. + SuppressionInfo supression_info(base::Time::Now(), + config_.suppression_duration); + client_state->suppression_info = std::move(supression_info); + client_state->current_max_daily_show = 0; +} + +void ImpressionHistoryTrackerImpl::CheckSuppressionExpiration( + ClientState* client_state) { + // No suppression to recover from. + if (!client_state->suppression_info.has_value()) + return; + + SuppressionInfo& suppression = client_state->suppression_info.value(); + base::Time now = base::Time::Now(); + + // Still in the suppression time window. + if (now - suppression.last_trigger_time < suppression.duration) + return; + + // Recover from suppression and increase |current_max_daily_show|. + DCHECK_EQ(client_state->current_max_daily_show, 0); + client_state->current_max_daily_show = suppression.recover_goal; + + // Clear suppression if fully recovered. + client_state->suppression_info.reset(); + SetNeedsUpdate(client_state->type, true); +} + +bool ImpressionHistoryTrackerImpl::MaybeUpdateDb(SchedulerClientType type) { + auto it = client_states_.find(type); + if (it == client_states_.end()) + return false; + + bool db_updated = false; + if (NeedsUpdate(type)) { + store_->Update(ToDatabaseKey(type), *(it->second.get()), base::DoNothing()); + db_updated = true; + } + SetNeedsUpdate(type, false); + return db_updated; +} + +bool ImpressionHistoryTrackerImpl::MaybeUpdateAllDb() { + bool db_updated = false; + for (const auto& client_state : client_states_) { + auto type = client_state.second->type; + db_updated |= MaybeUpdateDb(type); + } + + return db_updated; +} + +void ImpressionHistoryTrackerImpl::SetNeedsUpdate(SchedulerClientType type, + bool needs_update) { + need_update_db_[type] = needs_update; +} + +bool ImpressionHistoryTrackerImpl::NeedsUpdate(SchedulerClientType type) const { + auto it = need_update_db_.find(type); + return it == need_update_db_.end() ? false : it->second; +} + +void ImpressionHistoryTrackerImpl::NotifyImpressionUpdate() { + if (delegate_) + delegate_->OnImpressionUpdated(); +} + +Impression* ImpressionHistoryTrackerImpl::FindImpressionNeedsUpdate( + const std::string& notification_guid) { + auto it = impression_map_.find(notification_guid); + if (it == impression_map_.end()) + return nullptr; + auto* impression = it->second; + + if (impression->integrated) + return nullptr; + + return it->second; +} + +void ImpressionHistoryTrackerImpl::OnClickInternal( + const std::string& notification_guid, + bool update_db) { + auto* impression = FindImpressionNeedsUpdate(notification_guid); + if (!impression) + return; + + auto it = client_states_.find(impression->type); + if (it == client_states_.end()) + return; + ClientState* client_state = it->second.get(); + ApplyPositiveImpression(client_state, impression); + impression->feedback = UserFeedback::kClick; + + if (update_db && MaybeUpdateDb(client_state->type)) + NotifyImpressionUpdate(); +} + +void ImpressionHistoryTrackerImpl::OnButtonClickInternal( + const std::string& notification_guid, + ActionButtonType button_type, + bool update_db) { + auto* impression = FindImpressionNeedsUpdate(notification_guid); + if (!impression) + return; + auto it = client_states_.find(impression->type); + if (it == client_states_.end()) + return; + + ClientState* client_state = it->second.get(); + switch (button_type) { + case ActionButtonType::kHelpful: + ApplyPositiveImpression(client_state, impression); + impression->feedback = UserFeedback::kHelpful; + break; + case ActionButtonType::kUnhelpful: + ApplyNegativeImpression(client_state, impression); + impression->feedback = UserFeedback::kNotHelpful; + break; + case ActionButtonType::kUnknownAction: + NOTIMPLEMENTED(); + break; + } + + if (update_db && MaybeUpdateDb(client_state->type)) + NotifyImpressionUpdate(); +} + +void ImpressionHistoryTrackerImpl::OnDismissInternal( + const std::string& notification_guid, + bool update_db) { + auto* impression = FindImpressionNeedsUpdate(notification_guid); + if (!impression) + return; + + auto it = client_states_.find(impression->type); + if (it == client_states_.end()) + return; + ClientState* client_state = it->second.get(); + + AnalyzeImpressionHistory(client_state); + + if (update_db && MaybeUpdateDb(client_state->type)) + NotifyImpressionUpdate(); +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h new file mode 100644 index 0000000..d3e41f3 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker.h
@@ -0,0 +1,163 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_HISTORY_TRACKER_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_HISTORY_TRACKER_H_ + +#include <deque> +#include <map> +#include <memory> +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "chrome/browser/notifications/scheduler/internal/collection_store.h" +#include "chrome/browser/notifications/scheduler/internal/impression_types.h" +#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h" +#include "chrome/browser/notifications/scheduler/public/user_action_handler.h" + +namespace notifications { + +// Provides functionalities to update notification impression history and adjust +// maximum daily notification shown to the user. +class ImpressionHistoryTracker : public UserActionHandler { + public: + using ClientStates = + std::map<SchedulerClientType, std::unique_ptr<ClientState>>; + using InitCallback = base::OnceCallback<void(bool)>; + + class Delegate { + public: + Delegate() = default; + virtual ~Delegate() = default; + + // Called when the impression data is updated. + virtual void OnImpressionUpdated() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(Delegate); + }; + + // Initializes the impression tracker. + virtual void Init(Delegate* delegate, InitCallback callback) = 0; + + // Analyzes the impression history for all notification clients, and adjusts + // the |current_max_daily_show|. + virtual void AnalyzeImpressionHistory() = 0; + + // Queries the client states. + virtual void GetClientStates( + std::map<SchedulerClientType, const ClientState*>* client_states) + const = 0; + + virtual ~ImpressionHistoryTracker() = default; + + protected: + ImpressionHistoryTracker() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTracker); +}; + +// An implementation of ImpressionHistoryTracker backed by a database. +class ImpressionHistoryTrackerImpl : public ImpressionHistoryTracker { + public: + explicit ImpressionHistoryTrackerImpl( + const SchedulerConfig& config, + std::unique_ptr<CollectionStore<ClientState>> store); + ~ImpressionHistoryTrackerImpl() override; + + private: + // ImpressionHistoryTracker implementation. + void Init(Delegate* delegate, InitCallback callback) override; + void AnalyzeImpressionHistory() override; + void GetClientStates(std::map<SchedulerClientType, const ClientState*>* + client_states) const override; + void OnClick(const std::string& notification_id) override; + void OnActionClick(const std::string& notification_id, + ActionButtonType button_type) override; + void OnDismiss(const std::string& notification_id) override; + + // Called after |store_| is initialized. + void OnStoreInitialized(InitCallback callback, + bool success, + CollectionStore<ClientState>::Entries entries); + + // Helper method to prune impressions created before |start_time|. Assumes + // |impressions| are sorted by creation time. + static void PruneImpressionByCreateTime(std::deque<Impression*>* impressions, + const base::Time& start_time); + + // Analyzes the impression history for a particular client. + void AnalyzeImpressionHistory(ClientState* client_state); + + // Applies a positive impression result to this notification type. + void ApplyPositiveImpression(ClientState* client_state, + Impression* impression); + + // Applies negative impression on this notification type when |num_actions| + // consecutive negative impression result are generated. + void ApplyNegativeImpressions(ClientState* client_state, + std::deque<Impression*>* impressions, + size_t num_actions); + + // Applies one negative impression. + void ApplyNegativeImpression(ClientState* client_state, + Impression* impression); + + // Checks if suppression is expired and recover to a certain daily quota. + void CheckSuppressionExpiration(ClientState* client_state); + + // Tries to update the database records for |type|. Returns whether the db is + // actually updated. + bool MaybeUpdateDb(SchedulerClientType type); + bool MaybeUpdateAllDb(); + + // Sets/Gets the flag if impression data for |type| needs update in the + // database. + void SetNeedsUpdate(SchedulerClientType type, bool needs_update); + bool NeedsUpdate(SchedulerClientType type) const; + + // Notifies the delegate about impression data update. + void NotifyImpressionUpdate(); + + // Finds an impression that needs to update based on notification id. + Impression* FindImpressionNeedsUpdate(const std::string& notification_guid); + + void OnClickInternal(const std::string& notification_guid, bool update_db); + void OnButtonClickInternal(const std::string& notification_guid, + ActionButtonType button_type, + bool update_db); + void OnDismissInternal(const std::string& notification_guid, bool update_db); + + // Impression history and global states for all notification scheduler + // clients. + ClientStates client_states_; + + // Notification guid to Impression map. + std::map<std::string, Impression*> impression_map_; + + // The storage that persists data. + std::unique_ptr<CollectionStore<ClientState>> store_; + + // System configuration. + const SchedulerConfig& config_; + + // Whether the impression tracker is successfully initialized. + bool initialized_; + + // If the database needs an update when any of the impression data is updated. + std::map<SchedulerClientType, bool> need_update_db_; + + Delegate* delegate_; + + base::WeakPtrFactory<ImpressionHistoryTrackerImpl> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerImpl); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_HISTORY_TRACKER_H_
diff --git a/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc b/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc new file mode 100644 index 0000000..26a89e3 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/impression_history_tracker_unittest.cc
@@ -0,0 +1,274 @@ +// Copyright 2019 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 <memory> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" +#include "chrome/browser/notifications/scheduler/internal/impression_history_tracker.h" +#include "chrome/browser/notifications/scheduler/test/test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using ::testing::Invoke; +using StoreEntries = std::vector<std::unique_ptr<notifications::ClientState>>; + +namespace notifications { +namespace { + +const char kGuid1[] = "guid1"; + +struct TestCase { + // Input data that will be pushed to the target class. + std::vector<test::ImpressionTestData> input; + + // Expected output data. + std::vector<test::ImpressionTestData> expected; +}; + +Impression CreateImpression(const base::Time& create_time, + const std::string& guid) { + return {create_time, + UserFeedback::kNoFeedback, + ImpressionResult::kInvalid, + false /* integrated */, + SchedulerTaskTime::kMorning, + guid, + SchedulerClientType::kTest1}; +} + +TestCase CreateDefaultTestCase() { + TestCase test_case; + test_case.input = {{SchedulerClientType::kTest1, + 2 /* current_max_daily_show */, + {}, + base::nullopt /* suppression_info */}}; + test_case.expected = test_case.input; + return test_case; +} + +class MockImpressionStore : public CollectionStore<ClientState> { + public: + MockImpressionStore() {} + + MOCK_METHOD1(InitAndLoad, void(CollectionStore<ClientState>::LoadCallback)); + MOCK_METHOD3(Add, + void(const std::string&, + const ClientState&, + base::OnceCallback<void(bool)>)); + MOCK_METHOD3(Update, + void(const std::string&, + const ClientState&, + base::OnceCallback<void(bool)>)); + MOCK_METHOD2(Delete, + void(const std::string&, base::OnceCallback<void(bool)>)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockImpressionStore); +}; + +class MockDelegate : public ImpressionHistoryTracker::Delegate { + public: + MockDelegate() = default; + ~MockDelegate() = default; + MOCK_METHOD0(OnImpressionUpdated, void()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockDelegate); +}; + +// TODO(xingliu): Add more test cases following the test doc. +class ImpressionHistoryTrackerTest : public ::testing::Test { + public: + ImpressionHistoryTrackerTest() : store_(nullptr), delegate_(nullptr) {} + ~ImpressionHistoryTrackerTest() override = default; + + void SetUp() override { + config_.impression_expiration = base::TimeDelta::FromDays(28); + config_.suppression_duration = base::TimeDelta::FromDays(56); + } + + protected: + // Creates the tracker and push in data. + void InitTrackerWithData(const TestCase& test_case) { + StoreEntries entries; + test::AddImpressionTestData(test_case.input, &entries); + + auto store = std::make_unique<MockImpressionStore>(); + store_ = store.get(); + delegate_ = std::make_unique<MockDelegate>(); + impression_trakcer_ = std::make_unique<ImpressionHistoryTrackerImpl>( + config_, std::move(store)); + + // Initialize the store and call the callback. + EXPECT_CALL(*store_, InitAndLoad(_)) + .WillOnce( + Invoke([&entries](base::OnceCallback<void(bool, StoreEntries)> cb) { + std::move(cb).Run(true, std::move(entries)); + })); + base::RunLoop loop; + impression_trakcer_->Init( + delegate_.get(), base::BindOnce( + [](base::RepeatingClosure closure, bool success) { + EXPECT_TRUE(success); + std::move(closure).Run(); + }, + loop.QuitClosure())); + loop.Run(); + } + + // Verifies the |expected_test_data| matches the internal states. + void VerifyClientStates(const TestCase& test_case) { + std::map<SchedulerClientType, const ClientState*> client_states; + impression_trakcer_->GetClientStates(&client_states); + + ImpressionHistoryTracker::ClientStates expected_client_states; + test::AddImpressionTestData(test_case.expected, &expected_client_states); + + DCHECK_EQ(expected_client_states.size(), client_states.size()); + for (const auto& expected : expected_client_states) { + auto output_it = client_states.find(expected.first); + DCHECK(output_it != client_states.end()); + EXPECT_EQ(*expected.second, *output_it->second) + << "Unmatch client states: \n" + << "Expected: \n" + << expected.second->DebugPrint() << " \n" + << "Acutual: \n" + << output_it->second->DebugPrint(); + } + } + + const SchedulerConfig& config() const { return config_; } + MockImpressionStore* store() { return store_; } + MockDelegate* delegate() { return delegate_.get(); } + ImpressionHistoryTracker* tracker() { return impression_trakcer_.get(); } + + private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + SchedulerConfig config_; + std::unique_ptr<ImpressionHistoryTracker> impression_trakcer_; + MockImpressionStore* store_; + std::unique_ptr<MockDelegate> delegate_; + + DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerTest); +}; + +// Verifies expired impression will be deleted. +TEST_F(ImpressionHistoryTrackerTest, DeleteExpiredImpression) { + TestCase test_case; + auto expired_create_time = base::Time::Now() - base::TimeDelta::FromDays(1) - + config().impression_expiration; + auto not_expired_time = base::Time::Now() + base::TimeDelta::FromDays(1) - + config().impression_expiration; + Impression expired{expired_create_time, UserFeedback::kNoFeedback, + ImpressionResult::kInvalid, false /* integrated */, + SchedulerTaskTime::kMorning, "guid1", + SchedulerClientType::kTest1}; + Impression not_expired{not_expired_time, + UserFeedback::kNoFeedback, + ImpressionResult::kInvalid, + false /* integrated */, + SchedulerTaskTime::kMorning, + "guid2", + SchedulerClientType::kTest1}; + + // The impressions in the input should be sorted by creation time when gets + // loaded to memory. + test_case.input = {{SchedulerClientType::kTest1, + 2 /* current_max_daily_show */, + {expired, not_expired, expired}, + base::nullopt /* suppression_info */}}; + + // Expired impression created in |expired_create_time| should be deleted. + // No change expected on the next impression, which is not expired and no user + // feedback . + test_case.expected = {{SchedulerClientType::kTest1, + 2 /* current_max_daily_show */, + {not_expired}, + base::nullopt /* suppression_info */}}; + + InitTrackerWithData(test_case); + EXPECT_CALL(*store(), Update(_, _, _)); + EXPECT_CALL(*delegate(), OnImpressionUpdated()); + tracker()->AnalyzeImpressionHistory(); + VerifyClientStates(test_case); +} + +// If impression has been deleted, click should have no result. +TEST_F(ImpressionHistoryTrackerTest, ClickNoImpression) { + TestCase test_case = CreateDefaultTestCase(); + InitTrackerWithData(test_case); + EXPECT_CALL(*store(), Update(_, _, _)).Times(0); + EXPECT_CALL(*delegate(), OnImpressionUpdated()).Times(0); + tracker()->OnClick(kGuid1); + VerifyClientStates(test_case); +} + +struct UserActionTestParam { + ImpressionResult impression_result = ImpressionResult::kInvalid; + UserFeedback user_feedback = UserFeedback::kNoFeedback; + int current_max_daily_show = 0; + base::Optional<ActionButtonType> button_type; + base::Optional<SuppressionInfo> suppression_info; +}; + +class ImpressionHistoryTrackerUserActionTest + : public ImpressionHistoryTrackerTest, + public ::testing::WithParamInterface<UserActionTestParam> { + public: + ImpressionHistoryTrackerUserActionTest() = default; + ~ImpressionHistoryTrackerUserActionTest() override = default; + + private: + DISALLOW_COPY_AND_ASSIGN(ImpressionHistoryTrackerUserActionTest); +}; + +// TODO(xingliu): Add test for unhelpful/dismiss, need to use base::Clock to +// mock base::Time::Now(). +const UserActionTestParam kUserActionTestParams[] = { + {ImpressionResult::kPositive, UserFeedback::kClick, 3, base::nullopt, + base::nullopt}, + {ImpressionResult::kPositive, UserFeedback::kHelpful, 3, + ActionButtonType::kHelpful, base::nullopt}}; + +// User actions like clicks should update the ClientState data accordingly. +TEST_P(ImpressionHistoryTrackerUserActionTest, UserAction) { + TestCase test_case = CreateDefaultTestCase(); + Impression impression = CreateImpression(base::Time::Now(), kGuid1); + DCHECK(!test_case.input.empty()); + test_case.input.front().impressions.emplace_back(impression); + + impression.impression = GetParam().impression_result; + impression.integrated = true; + impression.feedback = GetParam().user_feedback; + + test_case.expected.front().current_max_daily_show = + GetParam().current_max_daily_show; + test_case.expected.front().impressions.emplace_back(impression); + test_case.expected.front().suppression_info = GetParam().suppression_info; + + InitTrackerWithData(test_case); + EXPECT_CALL(*store(), Update(_, _, _)); + EXPECT_CALL(*delegate(), OnImpressionUpdated()); + + // Trigger user action. + if (GetParam().user_feedback == UserFeedback::kClick) + tracker()->OnClick(kGuid1); + else if (GetParam().button_type.has_value()) + tracker()->OnActionClick(kGuid1, GetParam().button_type.value()); + + VerifyClientStates(test_case); +} + +INSTANTIATE_TEST_SUITE_P(, + ImpressionHistoryTrackerUserActionTest, + testing::ValuesIn(kUserActionTestParams)); + +} // namespace + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/impression_store.cc b/chrome/browser/notifications/scheduler/internal/impression_store.cc new file mode 100644 index 0000000..c917b3d5 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/impression_store.cc
@@ -0,0 +1,100 @@ +// Copyright 2019 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/notifications/scheduler/internal/impression_store.h" + +#include "chrome/browser/notifications/scheduler/internal/proto_conversion.h" + +namespace leveldb_proto { + +void DataToProto(notifications::ClientState* client_state, + notifications::proto::ClientState* proto) { + ClientStateToProto(client_state, proto); +} + +void ProtoToData(notifications::proto::ClientState* proto, + notifications::ClientState* client_state) { + ClientStateFromProto(proto, client_state); +} + +} // namespace leveldb_proto + +namespace notifications { + +ImpressionStore::ImpressionStore( + std::unique_ptr< + leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>> db) + : db_(std::move(db)), weak_ptr_factory_(this) {} + +ImpressionStore::~ImpressionStore() = default; + +void ImpressionStore::InitAndLoad(LoadCallback callback) { + db_->Init(base::BindOnce(&ImpressionStore::OnDbInitialized, + weak_ptr_factory_.GetWeakPtr(), + std::move(callback))); +} + +void ImpressionStore::OnDbInitialized(LoadCallback callback, + leveldb_proto::Enums::InitStatus status) { + if (status != leveldb_proto::Enums::InitStatus::kOK) { + std::move(callback).Run(false, Entries()); + return; + } + + // Load the data after a successful initialization. + db_->LoadEntries(base::BindOnce(&ImpressionStore::OnDataLoaded, + weak_ptr_factory_.GetWeakPtr(), + std::move(callback))); +} + +void ImpressionStore::OnDataLoaded(LoadCallback callback, + bool success, + std::unique_ptr<EntryVector> entry_vector) { + // The database failed to load. + if (!success) { + std::move(callback).Run(false, Entries()); + return; + } + + // Success to load but no data. + if (!entry_vector) { + std::move(callback).Run(true, Entries()); + return; + } + + // Load data. + Entries entries; + for (auto it = entry_vector->begin(); it != entry_vector->end(); ++it) { + std::unique_ptr<ClientState> client_state = std::make_unique<ClientState>(); + *client_state = std::move(*it); + entries.emplace_back(std::move(client_state)); + } + + std::move(callback).Run(true, std::move(entries)); +} + +void ImpressionStore::Add(const std::string& key, + const ClientState& client_state, + UpdateCallback callback) { + Update(key, client_state, std::move(callback)); +} + +void ImpressionStore::Update(const std::string& key, + const ClientState& client_state, + UpdateCallback callback) { + auto entries_to_save = std::make_unique<KeyEntryVector>(); + entries_to_save->emplace_back(std::make_pair(key, client_state)); + db_->UpdateEntries(std::move(entries_to_save), + std::make_unique<KeyVector>() /*keys_to_remove*/, + std::move(callback)); +} + +void ImpressionStore::Delete(const std::string& key, UpdateCallback callback) { + auto keys_to_delete = std::make_unique<KeyVector>(); + keys_to_delete->emplace_back(key); + db_->UpdateEntries(std::make_unique<KeyEntryVector>() /*entries_to_save*/, + std::move(keys_to_delete), std::move(callback)); +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/impression_store.h b/chrome/browser/notifications/scheduler/internal/impression_store.h new file mode 100644 index 0000000..b7607d1 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/impression_store.h
@@ -0,0 +1,73 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_STORE_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_STORE_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/notifications/proto/client_state.pb.h" +#include "chrome/browser/notifications/scheduler/internal/collection_store.h" +#include "chrome/browser/notifications/scheduler/internal/impression_types.h" +#include "components/leveldb_proto/public/proto_database.h" + +// Forward declaration for proto conversion. +namespace leveldb_proto { +void DataToProto(notifications::ClientState* client_state, + notifications::proto::ClientState* proto); + +void ProtoToData(notifications::proto::ClientState* proto, + notifications::ClientState* client_state); +} // namespace leveldb_proto + +namespace notifications { + +// An impression storage using a proto database to persist data. +class ImpressionStore : public CollectionStore<ClientState> { + public: + ImpressionStore( + std::unique_ptr< + leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>> db); + ~ImpressionStore() override; + + private: + using KeyEntryVector = std::vector<std::pair<std::string, ClientState>>; + using KeyVector = std::vector<std::string>; + using EntryVector = std::vector<ClientState>; + + // CollectionStore implementation. + void InitAndLoad(LoadCallback callback) override; + void Add(const std::string& key, + const ClientState& client_state, + UpdateCallback callback) override; + void Update(const std::string& key, + const ClientState& client_state, + UpdateCallback callback) override; + void Delete(const std::string& key, UpdateCallback callback) override; + + // Called when the proto database is initialized but no yet loading the data + // into memory. + void OnDbInitialized(LoadCallback callback, + leveldb_proto::Enums::InitStatus status); + + // Called after loading the data from database. + void OnDataLoaded(LoadCallback callback, + bool success, + std::unique_ptr<EntryVector> entry_vector); + + std::unique_ptr<leveldb_proto::ProtoDatabase<proto::ClientState, ClientState>> + db_; + base::WeakPtrFactory<ImpressionStore> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ImpressionStore); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/internal/impression_store_unittest.cc b/chrome/browser/notifications/scheduler/internal/impression_store_unittest.cc new file mode 100644 index 0000000..edc2dd27 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/impression_store_unittest.cc
@@ -0,0 +1,207 @@ +// Copyright 2019 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/notifications/scheduler/internal/impression_store.h" + +#include <map> +#include <memory> +#include <string> +#include <utility> + +#include "base/test/scoped_task_environment.h" +#include "chrome/browser/notifications/proto/client_state.pb.h" +#include "chrome/browser/notifications/scheduler/internal/impression_types.h" +#include "chrome/browser/notifications/scheduler/internal/proto_conversion.h" +#include "components/leveldb_proto/public/proto_database.h" +#include "components/leveldb_proto/testing/fake_db.h" +#include "testing/gtest/include/gtest/gtest.h" + +using leveldb_proto::test::FakeDB; +using InitStatus = leveldb_proto::Enums::InitStatus; +using Entries = notifications::ImpressionStore::Entries; +using DbEntries = std::vector<notifications::ClientState>; +using DbEntriesPtr = std::unique_ptr<std::vector<notifications::ClientState>>; +using TestClientStates = std::map<std::string, notifications::ClientState>; + +namespace notifications { +namespace { + +const char kClientStateKey[] = "guid_client_state_key1"; +const ClientState kDefaultClientState; + +// Test fixture to verify impression store. +class ImpressionStoreTest : public testing::Test { + public: + ImpressionStoreTest() : load_result_(false), db_(nullptr) {} + ~ImpressionStoreTest() override = default; + + void SetUp() override {} + + protected: + // Initialize the store with test data. + void Init(const TestClientStates& test_data, InitStatus status) { + CreateTestProto(test_data); + + auto db = + std::make_unique<FakeDB<proto::ClientState, ClientState>>(&db_entries_); + db_ = db.get(); + store_ = std::make_unique<ImpressionStore>(std::move(db)); + store_->InitAndLoad(base::BindOnce(&ImpressionStoreTest::OnEntriesLoaded, + base::Unretained(this))); + db_->InitStatusCallback(status); + } + + bool load_result() const { return load_result_; } + const Entries& loaded_entries() const { return loaded_entries_; } + FakeDB<proto::ClientState, ClientState>* db() { return db_; } + + CollectionStore<ClientState>* store() { return store_.get(); } + + void OnEntriesLoaded(bool success, Entries entries) { + loaded_entries_ = std::move(entries); + load_result_ = success; + } + + // Verifies the entries in the db is |expected|. + void VerifyDataInDb(DbEntriesPtr expected) { + db_->LoadEntries(base::BindOnce( + [](DbEntriesPtr expected, bool success, DbEntriesPtr entries) { + EXPECT_TRUE(success); + DCHECK(entries); + DCHECK(expected); + EXPECT_EQ(entries->size(), expected->size()); + for (size_t i = 0, size = entries->size(); i < size; ++i) { + EXPECT_EQ((*entries)[i], (*expected)[i]); + } + }, + std::move(expected))); + db_->LoadCallback(true); + } + + private: + void CreateTestProto(const TestClientStates& client_states) { + for (const auto& pair : client_states) { + auto client_state(pair.second); + auto key = pair.first; + notifications::proto::ClientState proto; + ClientStateToProto(&client_state, &proto); + db_entries_.emplace(key, proto); + } + } + + base::test::ScopedTaskEnvironment scoped_task_environment_; + std::map<std::string, proto::ClientState> db_entries_; + bool load_result_; + Entries loaded_entries_; + + FakeDB<proto::ClientState, ClientState>* db_; + std::unique_ptr<CollectionStore<ClientState>> store_; + + DISALLOW_COPY_AND_ASSIGN(ImpressionStoreTest); +}; + +// Initializes an empty database should success. +TEST_F(ImpressionStoreTest, InitSuccessEmptyDb) { + Init(TestClientStates(), InitStatus::kOK); + db()->LoadCallback(true); + EXPECT_EQ(load_result(), true); + EXPECT_TRUE(loaded_entries().empty()); +} + +// Initialize non-empty database should success. +TEST_F(ImpressionStoreTest, InitSuccessWithData) { + auto test_data = TestClientStates(); + test_data.emplace(kClientStateKey, kDefaultClientState); + Init(test_data, InitStatus::kOK); + db()->LoadCallback(true); + EXPECT_EQ(load_result(), true); + EXPECT_EQ(loaded_entries().size(), 1u); + EXPECT_EQ(*loaded_entries().back(), kDefaultClientState); +} + +// Failure when loading the data will result in error. +TEST_F(ImpressionStoreTest, InitSuccessLoadFailed) { + Init(TestClientStates(), InitStatus::kOK); + db()->LoadCallback(false); + EXPECT_EQ(load_result(), false); + EXPECT_TRUE(loaded_entries().empty()); +} + +// Failed database initialization will result in error. +TEST_F(ImpressionStoreTest, InitFailed) { + Init(TestClientStates(), InitStatus::kCorrupt); + EXPECT_EQ(load_result(), false); + EXPECT_TRUE(loaded_entries().empty()); +} + +// Verifies adding data. +TEST_F(ImpressionStoreTest, Add) { + Init(TestClientStates(), InitStatus::kOK); + db()->LoadCallback(true); + EXPECT_EQ(load_result(), true); + EXPECT_TRUE(loaded_entries().empty()); + + // Add data to the store. + store()->Add(kClientStateKey, kDefaultClientState, + base::BindOnce([](bool success) { EXPECT_TRUE(success); })); + db()->UpdateCallback(true); + + // Verify the new data is in the database. + auto expected = std::make_unique<DbEntries>(); + expected->emplace_back(kDefaultClientState); + VerifyDataInDb(std::move(expected)); +} + +// Verifies failure when adding data will result in error. +TEST_F(ImpressionStoreTest, AddFailed) { + Init(TestClientStates(), InitStatus::kOK); + db()->LoadCallback(true); + EXPECT_EQ(load_result(), true); + EXPECT_TRUE(loaded_entries().empty()); + + store()->Add(kClientStateKey, kDefaultClientState, + base::BindOnce([](bool success) { EXPECT_FALSE(success); })); + db()->UpdateCallback(false); +} + +// Verifies updating data. +TEST_F(ImpressionStoreTest, Update) { + auto test_data = TestClientStates(); + test_data.emplace(kClientStateKey, kDefaultClientState); + Init(test_data, InitStatus::kOK); + db()->LoadCallback(true); + EXPECT_EQ(load_result(), true); + + ClientState new_client_state; + new_client_state.current_max_daily_show = 100; + + // Update the database. + store()->Update(kClientStateKey, new_client_state, + base::BindOnce([](bool success) { EXPECT_TRUE(success); })); + db()->UpdateCallback(true); + + // Verify the updated data is in the database. + auto expected = std::make_unique<DbEntries>(); + expected->emplace_back(new_client_state); + VerifyDataInDb(std::move(expected)); +} + +// Verifies deleting data. +TEST_F(ImpressionStoreTest, Delete) { + auto test_data = TestClientStates(); + test_data.emplace(kClientStateKey, kDefaultClientState); + Init(test_data, InitStatus::kOK); + db()->LoadCallback(true); + EXPECT_EQ(load_result(), true); + + // Delete the entry. + store()->Delete(kClientStateKey, + base::BindOnce([](bool success) { EXPECT_TRUE(success); })); + + // Verify there is no data in the database. + VerifyDataInDb(std::make_unique<DbEntries>()); +} + +} // namespace +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/impression_types.cc b/chrome/browser/notifications/scheduler/internal/impression_types.cc new file mode 100644 index 0000000..9c4deb8 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/impression_types.cc
@@ -0,0 +1,86 @@ +// Copyright 2019 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/notifications/scheduler/internal/impression_types.h" + +#include <sstream> + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" + +namespace notifications { + +bool Impression::operator==(const Impression& other) const { + return create_time == other.create_time && feedback == other.feedback && + impression == other.impression && integrated == other.integrated && + task_start_time == other.task_start_time && guid == other.guid && + type == other.type; +} + +SuppressionInfo::SuppressionInfo(const base::Time& last_trigger, + const base::TimeDelta& duration) + : last_trigger_time(last_trigger), duration(duration), recover_goal(1) {} + +SuppressionInfo::SuppressionInfo(const SuppressionInfo& other) = default; + +bool SuppressionInfo::operator==(const SuppressionInfo& other) const { + return last_trigger_time == other.last_trigger_time && + duration == other.duration && recover_goal == other.recover_goal; +} + +base::Time SuppressionInfo::ReleaseTime() const { + return last_trigger_time + duration; +} + +ClientState::ClientState() + : type(SchedulerClientType::kUnknown), current_max_daily_show(0) {} + +ClientState::ClientState(const ClientState& other) = default; + +ClientState::~ClientState() = default; + +bool ClientState::operator==(const ClientState& other) const { + return type == other.type && + current_max_daily_show == other.current_max_daily_show && + impressions == other.impressions && + suppression_info == other.suppression_info; +} + +std::string ClientState::DebugPrint() const { + std::string log = base::StringPrintf( + "Client state: type: %d \n" + "current_max_daily_show: %d \n" + "impressions.size(): %zu \n", + static_cast<int>(type), current_max_daily_show, impressions.size()); + + for (const auto& impression : impressions) { + std::ostringstream stream; + stream << "Impression, create_time:" << impression.create_time << "\n" + << " create_time in microseconds:" + << impression.create_time.ToDeltaSinceWindowsEpoch().InMicroseconds() + << "\n" + << "feedback: " << static_cast<int>(impression.feedback) << "\n" + << "impression result: " << static_cast<int>(impression.impression) + << " \n" + << "integrated: " << impression.integrated << "\n" + << "task start time: " + << static_cast<int>(impression.task_start_time) << "\n" + << "guid: " << impression.guid << "\n" + << "type: " << static_cast<int>(impression.type); + log += stream.str(); + } + + if (suppression_info.has_value()) { + std::ostringstream stream; + stream << "Suppression info, last_trigger_time:" + << suppression_info->last_trigger_time << "\n" + << "duration:" << suppression_info->duration << "\n" + << "recover_goal:" << suppression_info->recover_goal; + log += stream.str(); + } + + return log; +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/impression_types.h b/chrome/browser/notifications/scheduler/internal/impression_types.h new file mode 100644 index 0000000..37a8173f6 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/impression_types.h
@@ -0,0 +1,110 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_TYPES_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_TYPES_H_ + +#include <deque> +#include <map> +#include <string> + +#include "base/optional.h" +#include "base/time/time.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" + +namespace notifications { + +// Contains data to determine when a notification should be shown to the user +// and the user impression towards this notification. +// +// Life cycle: +// 1. Created after the notification is shown to the user. +// 2. |feedback| is set after the user interacts with the notification. +// 3. Notification scheduler API consumer gets the user feedback and generates +// an impression result, which may affect notification exposure. +// 4. The impression is deleted after it expires. +struct Impression { + bool operator==(const Impression& other) const; + + // Creation timestamp. + base::Time create_time; + + // The user feedback on the notification, each notification will have at most + // one feedback. Sets after the user interacts with the notification. + UserFeedback feedback = UserFeedback::kNoFeedback; + + // The impression type. The client of a notification type takes one or several + // user feedbacks as input and generate a user impression, which will + // eventually affect the rate to deliver notifications to the user. + ImpressionResult impression = ImpressionResult::kInvalid; + + // If the user feedback is used in computing the current notification deliver + // rate. + bool integrated = false; + + // The task start time when this impression is generated. + SchedulerTaskTime task_start_time = SchedulerTaskTime::kUnknown; + + // The unique identifier of the notification. + std::string guid; + + // The type of the notification. Not persisted to disk, set after database + // initialized. + // TODO(xingliu): Consider to persist this as well. + SchedulerClientType type = SchedulerClientType::kUnknown; +}; + +// Contains details about supression and recovery after suppression expired. +struct SuppressionInfo { + SuppressionInfo(const base::Time& last_trigger, + const base::TimeDelta& duration); + SuppressionInfo(const SuppressionInfo& other); + ~SuppressionInfo() = default; + bool operator==(const SuppressionInfo& other) const; + + // Time that the suppression should release. + base::Time ReleaseTime() const; + + // The last supression trigger time. + base::Time last_trigger_time; + + // The duration for the suppression. + base::TimeDelta duration; + + // |current_max_daily_show| will change to this after the suppression + // expired. + int recover_goal; +}; + +// Stores the global states about how often the notification can be shown +// to the user and the history of user interactions to a particular notification +// client. +struct ClientState { + using Impressions = std::deque<Impression>; + ClientState(); + explicit ClientState(const ClientState& other); + ~ClientState(); + + bool operator==(const ClientState& other) const; + + // Dumps data for debugging. + std::string DebugPrint() const; + + // The type of notification using the scheduler. + SchedulerClientType type; + + // The maximum number of notifications shown to the user for this type. May + // change if the user interacts with the notification. + int current_max_daily_show; + + // A list of user impression history. Sorted by creation time. + Impressions impressions; + + // Suppression details, no value if there is currently no suppression. + base::Optional<SuppressionInfo> suppression_info; +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_IMPRESSION_TYPES_H_
diff --git a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc new file mode 100644 index 0000000..70d1d16 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.cc
@@ -0,0 +1,56 @@ +// Copyright 2019 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/notifications/scheduler/internal/init_aware_scheduler.h" + +#include "base/bind.h" +#include "chrome/browser/notifications/scheduler/public/notification_params.h" + +namespace notifications { + +InitAwareNotificationScheduler::InitAwareNotificationScheduler( + std::unique_ptr<NotificationScheduler> impl) + : impl_(std::move(impl)), weak_ptr_factory_(this) {} + +InitAwareNotificationScheduler::~InitAwareNotificationScheduler() = default; + +void InitAwareNotificationScheduler::Init(InitCallback init_callback) { + DCHECK(!init_success_.has_value()); + impl_->Init(base::BindOnce(&InitAwareNotificationScheduler::OnInitialized, + weak_ptr_factory_.GetWeakPtr(), + std::move(init_callback))); +} +void InitAwareNotificationScheduler::Schedule( + std::unique_ptr<NotificationParams> params) { + if (init_success_.has_value() && *init_success_) { + impl_->Schedule(std::move(params)); + return; + } + + if (init_success_.has_value() && !*init_success_) + return; + + cached_closures_.emplace_back( + base::BindOnce(&InitAwareNotificationScheduler::Schedule, + weak_ptr_factory_.GetWeakPtr(), std::move(params))); +} + +void InitAwareNotificationScheduler::OnInitialized(InitCallback init_callback, + bool success) { + init_success_ = success; + if (!success) { + cached_closures_.clear(); + std::move(init_callback).Run(false); + return; + } + + // Flush all cached calls. + for (auto it = cached_closures_.begin(); it != cached_closures_.end(); ++it) { + std::move(*it).Run(); + } + cached_closures_.clear(); + std::move(init_callback).Run(true); +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h new file mode 100644 index 0000000..fde560e53 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h
@@ -0,0 +1,55 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_INIT_AWARE_SCHEDULER_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_INIT_AWARE_SCHEDULER_H_ + +#include <memory> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/optional.h" +#include "chrome/browser/notifications/scheduler/internal/notification_scheduler.h" + +namespace notifications { + +struct NotificationParams; + +// NotificationScheduler implementation that caches NotificationScheduler API +// calls and flush them to the actual implementation after initialization +// succeeded. +class InitAwareNotificationScheduler : public NotificationScheduler { + public: + explicit InitAwareNotificationScheduler( + std::unique_ptr<NotificationScheduler> impl); + ~InitAwareNotificationScheduler() override; + + private: + // NotificationScheduler implementation. + void Init(InitCallback init_callback) override; + void Schedule( + std::unique_ptr<NotificationParams> notification_params) override; + + // Called after initialization is done. + void OnInitialized(InitCallback init_callback, bool success); + + // Whether the initialization is successful. No value if initialization is not + // finished. + base::Optional<bool> init_success_; + + // Cached calls. + std::vector<base::OnceClosure> cached_closures_; + + // Actual implementation. + std::unique_ptr<NotificationScheduler> impl_; + + base::WeakPtrFactory<InitAwareNotificationScheduler> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(InitAwareNotificationScheduler); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_INIT_AWARE_SCHEDULER_H_
diff --git a/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc new file mode 100644 index 0000000..8c9b690 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/init_aware_scheduler_unittest.cc
@@ -0,0 +1,127 @@ +// Copyright 2019 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/notifications/scheduler/internal/init_aware_scheduler.h" + +#include <memory> + +#include "base/bind_helpers.h" +#include "base/test/scoped_task_environment.h" +#include "chrome/browser/notifications/scheduler/public/notification_params.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::InSequence; +using testing::Invoke; + +namespace notifications { +namespace { + +class MockNotificationScheduler : public NotificationScheduler { + public: + MockNotificationScheduler() = default; + ~MockNotificationScheduler() override = default; + + MOCK_METHOD1(Init, void(InitCallback)); + MOCK_METHOD1(Schedule, void(std::unique_ptr<NotificationParams>)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockNotificationScheduler); +}; + +class InitAwareNotificationSchedulerTest : public testing::Test { + public: + InitAwareNotificationSchedulerTest() : scheduler_impl_(nullptr) {} + ~InitAwareNotificationSchedulerTest() override = default; + + void SetUp() override { + auto scheduler = std::make_unique<MockNotificationScheduler>(); + scheduler_impl_ = scheduler.get(); + init_aware_scheduler_ = + std::make_unique<InitAwareNotificationScheduler>(std::move(scheduler)); + } + + protected: + std::unique_ptr<NotificationParams> BuildParams() { + return std::make_unique<NotificationParams>( + SchedulerClientType::kUnknown, NotificationData(), ScheduleParams()); + } + + NotificationScheduler* init_aware_scheduler() { + return init_aware_scheduler_.get(); + } + MockNotificationScheduler* scheduler_impl() { return scheduler_impl_; } + + private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + MockNotificationScheduler* scheduler_impl_; + std::unique_ptr<NotificationScheduler> init_aware_scheduler_; + + DISALLOW_COPY_AND_ASSIGN(InitAwareNotificationSchedulerTest); +}; + +// Checks std::unique_ptr<NotificationParams> has specific guid. +MATCHER_P(GuidIs, expected_guid, "") { + return arg->guid == expected_guid; +} + +// Verifies cached calls are flushed into the actual implementation. +TEST_F(InitAwareNotificationSchedulerTest, FlushCachedCalls) { + auto params = BuildParams(); + std::string guid = params->guid; + EXPECT_FALSE(guid.empty()); + { + InSequence sequence; + EXPECT_CALL(*scheduler_impl(), Init(_)) + .WillOnce(Invoke([](NotificationScheduler::InitCallback cb) { + std::move(cb).Run(true /*success*/); + })); + EXPECT_CALL(*scheduler_impl(), Schedule(GuidIs(guid))); + + // Schedule() call before Init() will be cached. + init_aware_scheduler()->Schedule(std::move(params)); + init_aware_scheduler()->Init(base::DoNothing()); + } +} + +// Verifies that API calls after successful initialization will call into the +// actual implementation. +TEST_F(InitAwareNotificationSchedulerTest, CallAfterInitSuccess) { + auto params = BuildParams(); + std::string guid = params->guid; + EXPECT_FALSE(guid.empty()); + { + InSequence sequence; + EXPECT_CALL(*scheduler_impl(), Init(_)) + .WillOnce(Invoke([](NotificationScheduler::InitCallback cb) { + std::move(cb).Run(true /*success*/); + })); + EXPECT_CALL(*scheduler_impl(), Schedule(GuidIs(guid))); + + // Schedule() call after Init(). + init_aware_scheduler()->Init(base::DoNothing()); + init_aware_scheduler()->Schedule(std::move(params)); + } +} + +// Verifies no calls are flushed to actual implementation if initialization +// failed. +TEST_F(InitAwareNotificationSchedulerTest, NoFlushOnInitFailure) { + auto params1 = BuildParams(); + auto params2 = BuildParams(); + + EXPECT_CALL(*scheduler_impl(), Init(_)) + .WillOnce(Invoke([](NotificationScheduler::InitCallback cb) { + std::move(cb).Run(false /*success*/); + })); + EXPECT_CALL(*scheduler_impl(), Schedule(_)).Times(0); + + init_aware_scheduler()->Schedule(std::move(params1)); + init_aware_scheduler()->Init(base::DoNothing()); + init_aware_scheduler()->Schedule(std::move(params2)); +} + +} // namespace +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/notification_entry.cc b/chrome/browser/notifications/scheduler/internal/notification_entry.cc new file mode 100644 index 0000000..f793058 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/notification_entry.cc
@@ -0,0 +1,29 @@ +// Copyright 2019 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/notifications/scheduler/internal/notification_entry.h" + +#include <utility> + +namespace notifications { + +NotificationEntry::NotificationEntry() + : NotificationEntry(SchedulerClientType::kUnknown, std::string()) {} + +NotificationEntry::NotificationEntry(SchedulerClientType type, + const std::string& guid) + : type(type), guid(guid), create_time(base::Time::Now()) {} + +NotificationEntry::NotificationEntry(const NotificationEntry& other) = default; + +bool NotificationEntry::operator==(const NotificationEntry& other) const { + return type == other.type && guid == other.guid && + create_time == other.create_time && + notification_data == other.notification_data && + schedule_params == other.schedule_params; +} + +NotificationEntry::~NotificationEntry() = default; + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/notification_entry.h b/chrome/browser/notifications/scheduler/internal/notification_entry.h new file mode 100644 index 0000000..db27e00a --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/notification_entry.h
@@ -0,0 +1,44 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_ENTRY_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_ENTRY_H_ + +#include <string> + +#include "base/time/time.h" +#include "chrome/browser/notifications/scheduler/public/notification_data.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" +#include "chrome/browser/notifications/scheduler/public/schedule_params.h" + +namespace notifications { + +// Represents the in-memory counterpart of scheduled notification database +// record. +struct NotificationEntry { + NotificationEntry(); + NotificationEntry(SchedulerClientType type, const std::string& guid); + NotificationEntry(const NotificationEntry& other); + bool operator==(const NotificationEntry& other) const; + ~NotificationEntry(); + + // The type of the notification. + SchedulerClientType type; + + // The unique id of the notification database entry. + std::string guid; + + // Creation timestamp. + base::Time create_time; + + // Contains information to construct the notification. + NotificationData notification_data; + + // Scheduling details. + ScheduleParams schedule_params; +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_ENTRY_H_
diff --git a/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc new file mode 100644 index 0000000..da199db4 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.cc
@@ -0,0 +1,26 @@ +// Copyright 2019 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/notifications/scheduler/internal/notification_schedule_service_impl.h" + +#include <utility> + +#include "base/logging.h" +#include "chrome/browser/notifications/scheduler/internal/notification_scheduler.h" +#include "chrome/browser/notifications/scheduler/public/notification_params.h" + +namespace notifications { + +NotificationScheduleServiceImpl::NotificationScheduleServiceImpl( + std::unique_ptr<NotificationScheduler> scheduler) + : scheduler_(std::move(scheduler)) {} + +NotificationScheduleServiceImpl::~NotificationScheduleServiceImpl() = default; + +void NotificationScheduleServiceImpl::Schedule( + std::unique_ptr<NotificationParams> notification_params) { + scheduler_->Schedule(std::move(notification_params)); +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h new file mode 100644 index 0000000..802712c --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h
@@ -0,0 +1,37 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULE_SERVICE_IMPL_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULE_SERVICE_IMPL_H_ + +#include <memory> + +#include "base/macros.h" +#include "chrome/browser/notifications/scheduler/public/notification_schedule_service.h" + +namespace notifications { + +class NotificationScheduler; +struct NotificationParams; + +class NotificationScheduleServiceImpl : public NotificationScheduleService { + public: + explicit NotificationScheduleServiceImpl( + std::unique_ptr<NotificationScheduler> scheduler); + ~NotificationScheduleServiceImpl() override; + + private: + // NotificationScheduleService implementation. + void Schedule( + std::unique_ptr<NotificationParams> notification_params) override; + + // Provides the actual notification scheduling functionalities. + std::unique_ptr<NotificationScheduler> scheduler_; + + DISALLOW_COPY_AND_ASSIGN(NotificationScheduleServiceImpl); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULE_SERVICE_IMPL_H_
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc b/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc new file mode 100644 index 0000000..7bdec94 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler.cc
@@ -0,0 +1,226 @@ +// Copyright 2019 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/notifications/scheduler/internal/notification_scheduler.h" + +#include <string> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/weak_ptr.h" +#include "base/optional.h" +#include "chrome/browser/notifications/scheduler/internal/display_decider.h" +#include "chrome/browser/notifications/scheduler/internal/distribution_policy.h" +#include "chrome/browser/notifications/scheduler/internal/icon_store.h" +#include "chrome/browser/notifications/scheduler/internal/impression_history_tracker.h" +#include "chrome/browser/notifications/scheduler/internal/notification_entry.h" +#include "chrome/browser/notifications/scheduler/internal/notification_scheduler_context.h" +#include "chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h" +#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h" +#include "chrome/browser/notifications/scheduler/public/notification_params.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h" +#include "chrome/browser/notifications/scheduler/public/user_action_handler.h" + +namespace notifications { +namespace { + +class NotificationSchedulerImpl; + +// Helper class to do async initialization in parallel for multiple subsystem +// instances. +class InitHelper { + public: + using InitCallback = base::OnceCallback<void(bool)>; + InitHelper() : weak_ptr_factory_(this) {} + + ~InitHelper() = default; + + // Initializes subsystems in notification scheduler, |callback| will be + // invoked if all initializations finished or anyone of them failed. The + // object should be destroyed along with the |callback|. + void Init( + NotificationSchedulerContext* context, + ScheduledNotificationManager::Delegate* notification_manager_delegate, + ImpressionHistoryTracker::Delegate* impression_tracker_delegate, + InitCallback callback) { + callback_ = std::move(callback); + context->icon_store()->Init(base::BindOnce( + &InitHelper::OnIconStoreInitialized, weak_ptr_factory_.GetWeakPtr())); + context->impression_tracker()->Init( + impression_tracker_delegate, + base::BindOnce(&InitHelper::OnImpressionTrackerInitialized, + weak_ptr_factory_.GetWeakPtr())); + context->notification_manager()->Init( + notification_manager_delegate, + base::BindOnce(&InitHelper::OnNotificationManagerInitialized, + weak_ptr_factory_.GetWeakPtr())); + } + + private: + void OnIconStoreInitialized(bool success) { + icon_store_initialzed_ = success; + MaybeFinishInitialization(); + } + + void OnImpressionTrackerInitialized(bool success) { + impression_tracker_initialzed_ = success; + MaybeFinishInitialization(); + } + + void OnNotificationManagerInitialized(bool success) { + notification_manager_initialized_ = success; + MaybeFinishInitialization(); + } + + void MaybeFinishInitialization() { + bool all_finished = icon_store_initialzed_.has_value() && + impression_tracker_initialzed_.has_value() && + notification_manager_initialized_.has_value(); + // Notify the initialization result when all subcomponents are initialized. + if (!all_finished) + return; + + bool success = icon_store_initialzed_.value() && + impression_tracker_initialzed_.value() && + notification_manager_initialized_.value(); + std::move(callback_).Run(success); + } + + InitCallback callback_; + base::Optional<bool> icon_store_initialzed_; + base::Optional<bool> impression_tracker_initialzed_; + base::Optional<bool> notification_manager_initialized_; + + base::WeakPtrFactory<InitHelper> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(InitHelper); +}; + +// Implementation of NotificationScheduler. +class NotificationSchedulerImpl + : public NotificationScheduler, + public NotificationBackgroundTaskScheduler::Handler, + public ScheduledNotificationManager::Delegate, + public ImpressionHistoryTracker::Delegate, + public UserActionHandler { + public: + NotificationSchedulerImpl( + std::unique_ptr<NotificationSchedulerContext> context) + : context_(std::move(context)), weak_ptr_factory_(this) {} + + ~NotificationSchedulerImpl() override = default; + + private: + // NotificationScheduler implementation. + void Init(InitCallback init_callback) override { + auto helper = std::make_unique<InitHelper>(); + auto* helper_ptr = helper.get(); + helper_ptr->Init( + context_.get(), this, this, + base::BindOnce(&NotificationSchedulerImpl::OnInitialized, + weak_ptr_factory_.GetWeakPtr(), std::move(helper), + std::move(init_callback))); + } + + void Schedule( + std::unique_ptr<NotificationParams> notification_params) override { + context_->notification_manager()->ScheduleNotification( + std::move(notification_params)); + } + + void OnInitialized(std::unique_ptr<InitHelper>, + InitCallback init_callback, + bool success) { + // TODO(xingliu): Inform the clients about initialization results and tear + // down internal components. + std::move(init_callback).Run(success); + } + + // NotificationBackgroundTaskScheduler::Handler implementation. + void OnStartTask() override { + // Updates the impression data to compute daily notification shown budget. + context_->impression_tracker()->AnalyzeImpressionHistory(); + + // TODO(xingliu): Pass SchedulerTaskTime from background task. + FindNotificationToShow(SchedulerTaskTime::kMorning); + + // Schedule the next background task based on scheduled notifications. + ScheduleBackgroundTask(); + } + + void OnStopTask() override { ScheduleBackgroundTask(); } + + // ScheduledNotificationManager::Delegate implementation. + void DisplayNotification( + std::unique_ptr<NotificationEntry> notification) override { + // TODO(xingliu): Inform the clients and show the notification. + NOTIMPLEMENTED(); + } + + // ImpressionHistoryTracker::Delegate implementation. + void OnImpressionUpdated() override { ScheduleBackgroundTask(); } + + void FindNotificationToShow(SchedulerTaskTime task_start_time) { + DisplayDecider::Results results; + ScheduledNotificationManager::Notifications notifications; + context_->notification_manager()->GetAllNotifications(¬ifications); + + DisplayDecider::ClientStates client_states; + context_->impression_tracker()->GetClientStates(&client_states); + + std::vector<SchedulerClientType> clients; + context_->client_registrar()->GetRegisteredClients(&clients); + + context_->display_decider()->FindNotificationsToShow( + context_->config(), std::move(clients), DistributionPolicy::Create(), + task_start_time, std::move(notifications), std::move(client_states), + &results); + + // TODO(xingliu): Update impression data after notification shown. + // See https://crbug.com/965133. + for (const auto& guid : results) { + context_->notification_manager()->DisplayNotification(guid); + } + } + + void ScheduleBackgroundTask() { + // TODO(xingliu): Implements a class to determine the next background task + // based on scheduled notification data. + NOTIMPLEMENTED(); + } + + void OnClick(const std::string& notification_id) override { + context_->impression_tracker()->OnClick(notification_id); + } + + void OnActionClick(const std::string& notification_id, + ActionButtonType button_type) override { + context_->impression_tracker()->OnActionClick(notification_id, button_type); + } + + void OnDismiss(const std::string& notification_id) override { + context_->impression_tracker()->OnDismiss(notification_id); + } + + std::unique_ptr<NotificationSchedulerContext> context_; + + base::WeakPtrFactory<NotificationSchedulerImpl> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerImpl); +}; + +} // namespace + +// static +std::unique_ptr<NotificationScheduler> NotificationScheduler::Create( + std::unique_ptr<NotificationSchedulerContext> context) { + return std::make_unique<NotificationSchedulerImpl>(std::move(context)); +} + +NotificationScheduler::NotificationScheduler() = default; + +NotificationScheduler::~NotificationScheduler() = default; + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler.h b/chrome/browser/notifications/scheduler/internal/notification_scheduler.h new file mode 100644 index 0000000..ad2054c7 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler.h
@@ -0,0 +1,43 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULER_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULER_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/macros.h" + +namespace notifications { + +class NotificationSchedulerContext; +struct NotificationParams; + +// Provides notification scheduling and throttling functionalities. This class +// glues all the subsystems together for notification scheduling system. +class NotificationScheduler { + public: + using InitCallback = base::OnceCallback<void(bool)>; + static std::unique_ptr<NotificationScheduler> Create( + std::unique_ptr<NotificationSchedulerContext> context); + + NotificationScheduler(); + virtual ~NotificationScheduler(); + + // Initializes the scheduler. + virtual void Init(InitCallback init_callback) = 0; + + // Schedules a notification to show in the future. Throttling logic may apply + // based on |notification_params|. + virtual void Schedule( + std::unique_ptr<NotificationParams> notification_params) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(NotificationScheduler); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULER_H_
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.cc b/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.cc new file mode 100644 index 0000000..b70faa5 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.cc
@@ -0,0 +1,36 @@ +// Copyright 2019 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/notifications/scheduler/internal/notification_scheduler_context.h" + +#include <utility> + +#include "chrome/browser/notifications/scheduler/internal/display_decider.h" +#include "chrome/browser/notifications/scheduler/internal/icon_store.h" +#include "chrome/browser/notifications/scheduler/internal/impression_history_tracker.h" +#include "chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h" +#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h" +#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h" + +namespace notifications { + +NotificationSchedulerContext::NotificationSchedulerContext( + std::unique_ptr<NotificationSchedulerClientRegistrar> client_registrar, + std::unique_ptr<NotificationBackgroundTaskScheduler> scheduler, + std::unique_ptr<IconStore> icon_store, + std::unique_ptr<ImpressionHistoryTracker> impression_tracker, + std::unique_ptr<ScheduledNotificationManager> notification_manager, + std::unique_ptr<DisplayDecider> display_decider, + std::unique_ptr<SchedulerConfig> config) + : client_registrar_(std::move(client_registrar)), + background_task_scheduler_(std::move(scheduler)), + impression_tracker_(std::move(impression_tracker)), + notification_manager_(std::move(notification_manager)), + display_decider_(std::move(display_decider)), + config_(std::move(config)) {} + +NotificationSchedulerContext::~NotificationSchedulerContext() = default; + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.h b/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.h new file mode 100644 index 0000000..86aa260 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler_context.h
@@ -0,0 +1,88 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULER_CONTEXT_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULER_CONTEXT_H_ + +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" + +namespace notifications { + +class DisplayDecider; +class IconStore; +class ImpressionHistoryTracker; +class NotificationBackgroundTaskScheduler; +class NotificationSchedulerClientRegistrar; +class ScheduledNotificationManager; +struct SchedulerConfig; + +// Context that contains necessary components needed by the notification +// scheduler to perform tasks. +class NotificationSchedulerContext { + public: + NotificationSchedulerContext( + std::unique_ptr<NotificationSchedulerClientRegistrar> client_registrar, + std::unique_ptr<NotificationBackgroundTaskScheduler> scheduler, + std::unique_ptr<IconStore> icon_store, + std::unique_ptr<ImpressionHistoryTracker> impression_tracker, + std::unique_ptr<ScheduledNotificationManager> notification_manager, + std::unique_ptr<DisplayDecider> display_decider, + std::unique_ptr<SchedulerConfig> config); + ~NotificationSchedulerContext(); + + NotificationSchedulerClientRegistrar* client_registrar() { + return client_registrar_.get(); + } + + NotificationBackgroundTaskScheduler* background_task_scheduler() { + return background_task_scheduler_.get(); + } + + IconStore* icon_store() { return icon_store_.get(); } + + ImpressionHistoryTracker* impression_tracker() { + return impression_tracker_.get(); + } + + ScheduledNotificationManager* notification_manager() { + return notification_manager_.get(); + } + + DisplayDecider* display_decider() { return display_decider_.get(); } + + const SchedulerConfig* config() const { return config_.get(); } + + private: + // Holds a list of clients using the notification scheduler system. + std::unique_ptr<NotificationSchedulerClientRegistrar> client_registrar_; + + // Used to schedule background task in OS level. + std::unique_ptr<NotificationBackgroundTaskScheduler> + background_task_scheduler_; + + // Stores notification icons. + std::unique_ptr<IconStore> icon_store_; + + // Tracks user impressions towards specific notification type. + std::unique_ptr<ImpressionHistoryTracker> impression_tracker_; + + // Stores all scheduled notifications. + std::unique_ptr<ScheduledNotificationManager> notification_manager_; + + // Helper class to decide which notification should be displayed to the user. + std::unique_ptr<DisplayDecider> display_decider_; + + // System configuration. + std::unique_ptr<SchedulerConfig> config_; + + DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerContext); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_SCHEDULER_CONTEXT_H_
diff --git a/chrome/browser/notifications/scheduler/internal/notification_store.cc b/chrome/browser/notifications/scheduler/internal/notification_store.cc new file mode 100644 index 0000000..74c9dd1 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/notification_store.cc
@@ -0,0 +1,102 @@ +// Copyright 2019 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/notifications/scheduler/internal/notification_store.h" + +#include "base/bind.h" +#include "chrome/browser/notifications/scheduler/internal/proto_conversion.h" + +namespace leveldb_proto { +void DataToProto(notifications::NotificationEntry* entry, + notifications::proto::NotificationEntry* proto) { + NotificationEntryToProto(entry, proto); +} + +void ProtoToData(notifications::proto::NotificationEntry* proto, + notifications::NotificationEntry* entry) { + NotificationEntryFromProto(proto, entry); +} +} // namespace leveldb_proto + +namespace notifications { + +NotificationStore::NotificationStore( + std::unique_ptr<leveldb_proto::ProtoDatabase<proto::NotificationEntry, + NotificationEntry>> db) + : db_(std::move(db)), weak_ptr_factory_(this) {} + +NotificationStore::~NotificationStore() = default; + +void NotificationStore::InitAndLoad(LoadCallback callback) { + db_->Init(base::BindOnce(&NotificationStore::OnDbInitialized, + weak_ptr_factory_.GetWeakPtr(), + std::move(callback))); +} + +void NotificationStore::OnDbInitialized( + LoadCallback callback, + leveldb_proto::Enums::InitStatus status) { + if (status != leveldb_proto::Enums::InitStatus::kOK) { + std::move(callback).Run(false, Entries()); + return; + } + + // Load the data after a successful initialization. + db_->LoadEntries(base::BindOnce(&NotificationStore::OnDataLoaded, + weak_ptr_factory_.GetWeakPtr(), + std::move(callback))); +} + +void NotificationStore::OnDataLoaded( + LoadCallback callback, + bool success, + std::unique_ptr<EntryVector> entry_vector) { + // The database failed to load. + if (!success) { + std::move(callback).Run(false, Entries()); + return; + } + + // Success to load but no data. + if (!entry_vector) { + std::move(callback).Run(true, Entries()); + return; + } + + // Load data. + Entries entries; + for (auto it = entry_vector->begin(); it != entry_vector->end(); ++it) { + std::unique_ptr<NotificationEntry> notification_entry = + std::make_unique<NotificationEntry>(std::move(*it)); + entries.emplace_back(std::move(notification_entry)); + } + + std::move(callback).Run(true, std::move(entries)); +} + +void NotificationStore::Add(const std::string& key, + const NotificationEntry& entry, + UpdateCallback callback) { + Update(key, entry, std::move(callback)); +} + +void NotificationStore::Update(const std::string& key, + const NotificationEntry& entry, + UpdateCallback callback) { + auto entries_to_save = std::make_unique<KeyEntryVector>(); + entries_to_save->emplace_back(std::make_pair(key, entry)); + db_->UpdateEntries(std::move(entries_to_save), + std::make_unique<KeyVector>() /*keys_to_remove*/, + std::move(callback)); +} + +void NotificationStore::Delete(const std::string& key, + UpdateCallback callback) { + auto keys_to_delete = std::make_unique<KeyVector>(); + keys_to_delete->emplace_back(key); + db_->UpdateEntries(std::make_unique<KeyEntryVector>() /*entries_to_save*/, + std::move(keys_to_delete), std::move(callback)); +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/notification_store.h b/chrome/browser/notifications/scheduler/internal/notification_store.h new file mode 100644 index 0000000..cf032818 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/notification_store.h
@@ -0,0 +1,75 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_STORE_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_STORE_H_ + +#include <map> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/notifications/proto/notification_entry.pb.h" +#include "chrome/browser/notifications/scheduler/internal/collection_store.h" +#include "chrome/browser/notifications/scheduler/internal/notification_entry.h" +#include "components/leveldb_proto/public/proto_database.h" + +// Forward declaration for proto conversion. +namespace leveldb_proto { +void DataToProto(notifications::NotificationEntry* entry, + notifications::proto::NotificationEntry* proto); + +void ProtoToData(notifications::proto::NotificationEntry* proto, + notifications::NotificationEntry* entry); +} // namespace leveldb_proto + +namespace notifications { + +class NotificationStore : public CollectionStore<NotificationEntry> { + public: + NotificationStore( + std::unique_ptr<leveldb_proto::ProtoDatabase<proto::NotificationEntry, + NotificationEntry>> db); + ~NotificationStore() override; + + private: + using KeyEntryVector = std::vector<std::pair<std::string, NotificationEntry>>; + using KeyVector = std::vector<std::string>; + using EntryVector = std::vector<NotificationEntry>; + + // CollectionStore<NotificationEntry> implementation. + void InitAndLoad(LoadCallback callback) override; + void Add(const std::string& key, + const NotificationEntry& entry, + UpdateCallback callback) override; + void Update(const std::string& key, + const NotificationEntry& entry, + UpdateCallback callback) override; + void Delete(const std::string& key, UpdateCallback callback) override; + + // Called when the proto database is initialized but no yet loading the data + // into memory. + void OnDbInitialized(LoadCallback callback, + leveldb_proto::Enums::InitStatus status); + + // Called when data is loaded from |db_|. + void OnDataLoaded(LoadCallback callback, + bool success, + std::unique_ptr<EntryVector> entry_vector); + + // Level db instance to persist data. + std::unique_ptr< + leveldb_proto::ProtoDatabase<proto::NotificationEntry, NotificationEntry>> + db_; + + base::WeakPtrFactory<NotificationStore> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(NotificationStore); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_NOTIFICATION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/internal/notification_store_unittest.cc b/chrome/browser/notifications/scheduler/internal/notification_store_unittest.cc new file mode 100644 index 0000000..83f4517 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/notification_store_unittest.cc
@@ -0,0 +1,176 @@ +// Copyright 2019 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/notifications/scheduler/internal/notification_store.h" + +#include "base/test/scoped_task_environment.h" +#include "chrome/browser/notifications/scheduler/internal/proto_conversion.h" +#include "chrome/browser/notifications/scheduler/test/test_utils.h" +#include "components/leveldb_proto/public/proto_database.h" +#include "components/leveldb_proto/testing/fake_db.h" +#include "testing/gtest/include/gtest/gtest.h" + +using leveldb_proto::test::FakeDB; +using InitStatus = leveldb_proto::Enums::InitStatus; +using Entries = notifications::NotificationStore::Entries; +using TestNotificationEntries = + std::map<std::string, notifications::NotificationEntry>; +using DbEntries = std::vector<notifications::NotificationEntry>; +using DbEntriesPtr = + std::unique_ptr<std::vector<notifications::NotificationEntry>>; + +namespace notifications { +namespace { + +const char kGuid[] = "1234"; + +class NotificationStoreTest : public testing::Test { + public: + NotificationStoreTest() : load_result_(false) {} + ~NotificationStoreTest() override = default; + + void SetUp() override {} + + protected: + void Init(const TestNotificationEntries& test_data, InitStatus status) { + CreateTestProtos(test_data); + auto db = + std::make_unique<FakeDB<proto::NotificationEntry, NotificationEntry>>( + &db_protos_); + db_ = db.get(); + store_ = std::make_unique<NotificationStore>(std::move(db)); + store_->InitAndLoad(base::BindOnce(&NotificationStoreTest::OnEntriesLoaded, + base::Unretained(this))); + db_->InitStatusCallback(status); + } + + bool load_result() const { return load_result_; } + const Entries& loaded_entries() const { return loaded_entries_; } + FakeDB<proto::NotificationEntry, NotificationEntry>* db() { return db_; } + CollectionStore<NotificationEntry>* store() { return store_.get(); } + + void VerifyDataInDb(DbEntriesPtr expected) { + db_->LoadEntries(base::BindOnce( + [](DbEntriesPtr expected, bool success, DbEntriesPtr entries) { + EXPECT_TRUE(success); + DCHECK(entries); + DCHECK(expected); + EXPECT_EQ(entries->size(), expected->size()); + for (size_t i = 0, size = entries->size(); i < size; ++i) { + auto& entry = (*entries)[i]; + auto& expected_entry = (*expected)[i]; + EXPECT_EQ(entry, expected_entry) + << " \n Output: " << test::DebugString(&entry) + << " \n Expected: " << test::DebugString(&expected_entry); + } + }, + std::move(expected))); + db_->LoadCallback(true); + } + + private: + // Push data into |db_|. + void CreateTestProtos(const TestNotificationEntries& test_data) { + for (const auto& pair : test_data) { + const auto& key = pair.first; + auto entry = pair.second; + proto::NotificationEntry proto; + NotificationEntryToProto(&entry, &proto); + db_protos_.emplace(key, proto); + } + } + + void OnEntriesLoaded(bool success, Entries entries) { + load_result_ = success; + loaded_entries_ = std::move(entries); + } + + base::test::ScopedTaskEnvironment scoped_task_environment_; + + // Database test objects. + FakeDB<proto::NotificationEntry, NotificationEntry>* db_; + std::map<std::string, proto::NotificationEntry> db_protos_; + + std::unique_ptr<CollectionStore<NotificationEntry>> store_; + Entries loaded_entries_; + bool load_result_; + + DISALLOW_COPY_AND_ASSIGN(NotificationStoreTest); +}; + +// Verifies initialization with empty database. +TEST_F(NotificationStoreTest, Init) { + Init(TestNotificationEntries(), InitStatus::kOK); + db()->LoadCallback(true); + EXPECT_EQ(load_result(), true); + EXPECT_TRUE(loaded_entries().empty()); +} + +// Initialize non-empty database should success. +TEST_F(NotificationStoreTest, InitSuccessWithData) { + auto test_data = TestNotificationEntries(); + NotificationEntry entry(SchedulerClientType::kTest2, kGuid); + bool success = + base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time); + DCHECK(success); + test_data.emplace(kGuid, entry); + Init(test_data, InitStatus::kOK); + db()->LoadCallback(true); + EXPECT_EQ(load_result(), true); + EXPECT_EQ(loaded_entries().size(), 1u); + EXPECT_EQ(*loaded_entries().back(), entry); +} + +// Failed database initialization will result in error. +TEST_F(NotificationStoreTest, InitFailed) { + Init(TestNotificationEntries(), InitStatus::kCorrupt); + EXPECT_EQ(load_result(), false); + EXPECT_TRUE(loaded_entries().empty()); +} + +TEST_F(NotificationStoreTest, AddAndUpdate) { + Init(TestNotificationEntries(), InitStatus::kOK); + db()->LoadCallback(true); + EXPECT_EQ(load_result(), true); + EXPECT_TRUE(loaded_entries().empty()); + + NotificationEntry entry(SchedulerClientType::kTest2, kGuid); + bool success = + base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time); + DCHECK(success); + + // Add data to the store and verify the database. + store()->Add(kGuid, entry, + base::BindOnce([](bool success) { EXPECT_TRUE(success); })); + db()->UpdateCallback(true); + auto expected = std::make_unique<DbEntries>(); + expected->emplace_back(entry); + VerifyDataInDb(std::move(expected)); + + // Update and verified the new data. + entry.notification_data.title = "test_title"; + expected = std::make_unique<DbEntries>(); + expected->emplace_back(entry); + store()->Update(kGuid, entry, + base::BindOnce([](bool success) { EXPECT_TRUE(success); })); + db()->UpdateCallback(true); + VerifyDataInDb(std::move(expected)); +} + +TEST_F(NotificationStoreTest, Delete) { + auto test_data = TestNotificationEntries(); + NotificationEntry entry(SchedulerClientType::kTest2, kGuid); + test_data.emplace(kGuid, entry); + Init(test_data, InitStatus::kOK); + db()->LoadCallback(true); + EXPECT_EQ(loaded_entries().size(), 1u); + + // Delete the entry and verify data is deleted. + store()->Delete(kGuid, base::DoNothing()); + db()->UpdateCallback(true); + VerifyDataInDb(std::make_unique<DbEntries>()); +} + +} // namespace +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/proto_conversion.cc b/chrome/browser/notifications/scheduler/internal/proto_conversion.cc new file mode 100644 index 0000000..4fa2624 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/proto_conversion.cc
@@ -0,0 +1,327 @@ +// Copyright 2019 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/notifications/scheduler/internal/proto_conversion.h" + +#include <memory> +#include <utility> + +#include "base/logging.h" + +namespace notifications { + +namespace { + +// Helper method to convert base::TimeDelta to integer for serialization. Loses +// precision beyond miliseconds. +int64_t TimeDeltaToMilliseconds(const base::TimeDelta& delta) { + return delta.InMilliseconds(); +} + +// Helper method to convert serialized time delta as integer to base::TimeDelta +// for deserialization. Loses precision beyond miliseconds. +base::TimeDelta MillisecondsToTimeDelta(int64_t serialized_delat_ms) { + return base::TimeDelta::FromMilliseconds(serialized_delat_ms); +} + +// Helper method to convert base::Time to integer for serialization. Loses +// precision beyond miliseconds. +int64_t TimeToMilliseconds(const base::Time& time) { + return time.ToDeltaSinceWindowsEpoch().InMilliseconds(); +} + +// Helper method to convert serialized time as integer to base::Time for +// deserialization. Loses precision beyond miliseconds. +base::Time MillisecondsToTime(int64_t serialized_time_ms) { + return base::Time::FromDeltaSinceWindowsEpoch( + base::TimeDelta::FromMilliseconds(serialized_time_ms)); +} + +// Converts SchedulerClientType to its associated enum in proto buffer. +proto::SchedulerClientType ToSchedulerClientType(SchedulerClientType type) { + switch (type) { + case SchedulerClientType::kTest1: + return proto::SchedulerClientType::TEST_1; + case SchedulerClientType::kTest2: + return proto::SchedulerClientType::TEST_2; + case SchedulerClientType::kTest3: + return proto::SchedulerClientType::TEST_3; + case SchedulerClientType::kUnknown: + return proto::SchedulerClientType::UNKNOWN; + } + NOTREACHED(); +} + +// Converts SchedulerClientType from its associated enum in proto buffer. +SchedulerClientType FromSchedulerClientType( + proto::SchedulerClientType proto_type) { + switch (proto_type) { + case proto::SchedulerClientType::TEST_1: + return SchedulerClientType::kTest1; + case proto::SchedulerClientType::TEST_2: + return SchedulerClientType::kTest2; + case proto::SchedulerClientType::TEST_3: + return SchedulerClientType::kTest3; + case proto::SchedulerClientType::UNKNOWN: + return SchedulerClientType::kUnknown; + } + NOTREACHED(); +} + +// Converts UserFeedback to its associated enum in proto buffer. +proto::Impression_UserFeedback ToUserFeedback(UserFeedback feedback) { + switch (feedback) { + case UserFeedback::kNoFeedback: + return proto::Impression_UserFeedback_NO_FEEDBACK; + case UserFeedback::kHelpful: + return proto::Impression_UserFeedback_HELPFUL; + case UserFeedback::kNotHelpful: + return proto::Impression_UserFeedback_NOT_HELPFUL; + case UserFeedback::kClick: + return proto::Impression_UserFeedback_CLICK; + case UserFeedback::kDismiss: + return proto::Impression_UserFeedback_DISMISS; + case UserFeedback::kIgnore: + return proto::Impression_UserFeedback_IGNORE; + } + NOTREACHED(); +} + +// Converts UserFeedback from its associated enum in proto buffer. +UserFeedback FromUserFeedback(proto::Impression_UserFeedback feedback) { + switch (feedback) { + case proto::Impression_UserFeedback_NO_FEEDBACK: + return UserFeedback::kNoFeedback; + case proto::Impression_UserFeedback_HELPFUL: + return UserFeedback::kHelpful; + case proto::Impression_UserFeedback_NOT_HELPFUL: + return UserFeedback::kNotHelpful; + case proto::Impression_UserFeedback_CLICK: + return UserFeedback::kClick; + case proto::Impression_UserFeedback_DISMISS: + return UserFeedback::kDismiss; + case proto::Impression_UserFeedback_IGNORE: + return UserFeedback::kIgnore; + } + NOTREACHED(); +} + +// Converts ImpressionResult to its associated enum in proto buffer. +proto::Impression_ImpressionResult ToImpressionResult(ImpressionResult result) { + switch (result) { + case ImpressionResult::kInvalid: + return proto::Impression_ImpressionResult_INVALID; + case ImpressionResult::kPositive: + return proto::Impression_ImpressionResult_POSITIVE; + case ImpressionResult::kNegative: + return proto::Impression_ImpressionResult_NEGATIVE; + case ImpressionResult::kNeutral: + return proto::Impression_ImpressionResult_NEUTRAL; + } + NOTREACHED(); +} + +// Converts ImpressionResult from its associated enum in proto buffer. +ImpressionResult FromImpressionResult( + proto::Impression_ImpressionResult result) { + switch (result) { + case proto::Impression_ImpressionResult_INVALID: + return ImpressionResult::kInvalid; + case proto::Impression_ImpressionResult_POSITIVE: + return ImpressionResult::kPositive; + case proto::Impression_ImpressionResult_NEGATIVE: + return ImpressionResult::kNegative; + case proto::Impression_ImpressionResult_NEUTRAL: + return ImpressionResult::kNeutral; + } + NOTREACHED(); +} + +// Converts ImpressionResult to its associated enum in proto buffer. +proto::Impression_SchedulerTaskTime ToSchedulerTaskTime( + SchedulerTaskTime time) { + switch (time) { + case SchedulerTaskTime::kUnknown: + return proto::Impression_SchedulerTaskTime_UNKNOWN_TASK_TIME; + case SchedulerTaskTime::kMorning: + return proto::Impression_SchedulerTaskTime_MORNING; + case SchedulerTaskTime::kEvening: + return proto::Impression_SchedulerTaskTime_EVENING; + } + NOTREACHED(); +} + +// Converts ImpressionResult from its associated enum in proto buffer. +SchedulerTaskTime FromSchedulerTaskTime( + proto::Impression_SchedulerTaskTime time) { + switch (time) { + case proto::Impression_SchedulerTaskTime_UNKNOWN_TASK_TIME: + return SchedulerTaskTime::kUnknown; + case proto::Impression_SchedulerTaskTime_MORNING: + return SchedulerTaskTime::kMorning; + case proto::Impression_SchedulerTaskTime_EVENING: + return SchedulerTaskTime::kEvening; + } + NOTREACHED(); +} + +// Converts NotificationData to proto buffer type. +void NotificationDataToProto(NotificationData* notification_data, + proto::NotificationData* proto) { + proto->set_id(notification_data->id); + proto->set_title(notification_data->title); + proto->set_message(notification_data->message); + proto->set_icon_uuid(notification_data->icon_uuid); + proto->set_url(notification_data->url); +} + +// Converts NotificationData from proto buffer type. +void NotificationDataFromProto(proto::NotificationData* proto, + NotificationData* notification_data) { + notification_data->id = proto->id(); + notification_data->title = proto->title(); + notification_data->message = proto->message(); + notification_data->icon_uuid = proto->icon_uuid(); + notification_data->url = proto->url(); +} + +// Converts ScheduleParams::Priority to proto buffer type. +proto::ScheduleParams_Priority ScheduleParamsPriorityToProto( + ScheduleParams::Priority priority) { + using Priority = ScheduleParams::Priority; + switch (priority) { + case Priority::kLow: + return proto::ScheduleParams_Priority_LOW; + case Priority::kHigh: + return proto::ScheduleParams_Priority_HIGH; + case Priority::kNoThrottle: + return proto::ScheduleParams_Priority_NO_THROTTLE; + } +} + +// Converts ScheduleParams::Priority from proto buffer type. +ScheduleParams::Priority ScheduleParamsPriorityFromProto( + proto::ScheduleParams_Priority priority) { + using Priority = ScheduleParams::Priority; + switch (priority) { + case proto::ScheduleParams_Priority_LOW: + return Priority::kLow; + case proto::ScheduleParams_Priority_HIGH: + return Priority::kHigh; + case proto::ScheduleParams_Priority_NO_THROTTLE: + return Priority::kNoThrottle; + } +} + +// Converts ScheduleParams to proto buffer type. +void ScheduleParamsToProto(ScheduleParams* params, + proto::ScheduleParams* proto) { + proto->set_priority(ScheduleParamsPriorityToProto(params->priority)); +} + +// Converts ScheduleParams from proto buffer type. +void ScheduleParamsFromProto(proto::ScheduleParams* proto, + ScheduleParams* params) { + params->priority = ScheduleParamsPriorityFromProto(proto->priority()); +} + +} // namespace + +void IconEntryToProto(IconEntry* entry, notifications::proto::Icon* proto) { + proto->mutable_uuid()->swap(entry->uuid); + proto->mutable_icon()->swap(entry->data); +} + +void IconEntryFromProto(proto::Icon* proto, notifications::IconEntry* entry) { + DCHECK(proto->has_uuid()); + DCHECK(proto->has_icon()); + entry->data.swap(*proto->mutable_icon()); + entry->uuid.swap(*proto->mutable_uuid()); +} + +void ClientStateToProto(ClientState* client_state, + notifications::proto::ClientState* proto) { + proto->set_type(ToSchedulerClientType(client_state->type)); + proto->set_current_max_daily_show(client_state->current_max_daily_show); + + for (const auto& impression : client_state->impressions) { + auto* impression_ptr = proto->add_impressions(); + impression_ptr->set_create_time(TimeToMilliseconds(impression.create_time)); + impression_ptr->set_feedback(ToUserFeedback(impression.feedback)); + impression_ptr->set_impression(ToImpressionResult(impression.impression)); + impression_ptr->set_integrated(impression.integrated); + impression_ptr->set_task_start_time( + ToSchedulerTaskTime(impression.task_start_time)); + impression_ptr->set_guid(impression.guid); + } + + if (client_state->suppression_info.has_value()) { + const auto& suppression = *client_state->suppression_info; + auto* suppression_proto = proto->mutable_suppression_info(); + suppression_proto->set_last_trigger_time( + TimeToMilliseconds(suppression.last_trigger_time)); + suppression_proto->set_duration_ms( + TimeDeltaToMilliseconds(suppression.duration)); + suppression_proto->set_recover_goal(suppression.recover_goal); + } +} + +void ClientStateFromProto(proto::ClientState* proto, + notifications::ClientState* client_state) { + DCHECK(proto->has_type()); + DCHECK(proto->has_current_max_daily_show()); + client_state->type = FromSchedulerClientType(proto->type()); + client_state->current_max_daily_show = proto->current_max_daily_show(); + + for (const auto& proto_impression : proto->impressions()) { + Impression impression; + DCHECK(proto_impression.has_create_time()); + impression.create_time = MillisecondsToTime(proto_impression.create_time()); + impression.feedback = FromUserFeedback(proto_impression.feedback()); + impression.impression = FromImpressionResult(proto_impression.impression()); + impression.integrated = proto_impression.integrated(); + impression.task_start_time = + FromSchedulerTaskTime(proto_impression.task_start_time()); + impression.guid = proto_impression.guid(); + impression.type = client_state->type; + client_state->impressions.emplace_back(std::move(impression)); + } + + if (proto->has_suppression_info()) { + const auto& proto_suppression = proto->suppression_info(); + DCHECK(proto_suppression.has_last_trigger_time()); + DCHECK(proto_suppression.has_duration_ms()); + DCHECK(proto_suppression.has_recover_goal()); + + SuppressionInfo suppression_info( + MillisecondsToTime(proto_suppression.last_trigger_time()), + MillisecondsToTimeDelta(proto_suppression.duration_ms())); + suppression_info.recover_goal = proto_suppression.recover_goal(); + client_state->suppression_info = std::move(suppression_info); + } +} + +void NotificationEntryToProto(NotificationEntry* entry, + proto::NotificationEntry* proto) { + proto->set_type(ToSchedulerClientType(entry->type)); + proto->set_guid(entry->guid); + proto->set_create_time(TimeToMilliseconds(entry->create_time)); + auto* proto_notification_data = proto->mutable_notification_data(); + NotificationDataToProto(&entry->notification_data, proto_notification_data); + auto* proto_schedule_params = proto->mutable_schedule_params(); + ScheduleParamsToProto(&entry->schedule_params, proto_schedule_params); +} + +void NotificationEntryFromProto(proto::NotificationEntry* proto, + NotificationEntry* entry) { + entry->type = FromSchedulerClientType(proto->type()); + entry->guid = proto->guid(); + entry->create_time = MillisecondsToTime(proto->create_time()); + NotificationDataFromProto(proto->mutable_notification_data(), + &entry->notification_data); + ScheduleParamsFromProto(proto->mutable_schedule_params(), + &entry->schedule_params); +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/proto_conversion.h b/chrome/browser/notifications/scheduler/internal/proto_conversion.h new file mode 100644 index 0000000..382fc4e --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/proto_conversion.h
@@ -0,0 +1,44 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_PROTO_CONVERSION_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_PROTO_CONVERSION_H_ + +#include <memory> + +#include "base/macros.h" +#include "chrome/browser/notifications/proto/client_state.pb.h" +#include "chrome/browser/notifications/proto/icon.pb.h" +#include "chrome/browser/notifications/proto/notification_entry.pb.h" +#include "chrome/browser/notifications/scheduler/internal/icon_entry.h" +#include "chrome/browser/notifications/scheduler/internal/impression_types.h" +#include "chrome/browser/notifications/scheduler/internal/notification_entry.h" + +namespace notifications { + +// Converts an icon entry to icon proto. +void IconEntryToProto(IconEntry* entry, notifications::proto::Icon* proto); + +// Converts an icon proto to icon entry. +void IconEntryFromProto(proto::Icon* proto, notifications::IconEntry* entry); + +// Converts client state to proto. +void ClientStateToProto(ClientState* client_state, + notifications::proto::ClientState* proto); + +// Converts proto to client state. +void ClientStateFromProto(proto::ClientState* proto, + notifications::ClientState* client_state); + +// Converts notification entry to proto. +void NotificationEntryToProto(NotificationEntry* entry, + proto::NotificationEntry* proto); + +// Converts proto to notification entry. +void NotificationEntryFromProto(proto::NotificationEntry* proto, + NotificationEntry* entry); + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_PROTO_CONVERSION_H_
diff --git a/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc b/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc new file mode 100644 index 0000000..3bf7e31a --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/proto_conversion_unittest.cc
@@ -0,0 +1,189 @@ +// Copyright 2019 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/notifications/scheduler/internal/proto_conversion.h" + +#include <string> +#include <utility> +#include <vector> + +#include "base/logging.h" +#include "chrome/browser/notifications/scheduler/test/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +using IconProto = notifications::proto::Icon; + +namespace notifications { +namespace { + +const char kUuid[] = "123"; +const char kGuid[] = "testGuid"; +const char kData[] = "bitmapdata"; + +void TestClientStateConversion(ClientState* client_state) { + DCHECK(client_state); + notifications::proto::ClientState proto; + ClientState expected; + ClientStateToProto(client_state, &proto); + ClientStateFromProto(&proto, &expected); + EXPECT_EQ(*client_state, expected) + << " \n Output: \n " << client_state->DebugPrint() << " \n Expected: \n" + << expected.DebugPrint(); +} + +void TestNotificationEntryConversion(NotificationEntry* entry) { + DCHECK(entry); + notifications::proto::NotificationEntry proto; + NotificationEntry expected(SchedulerClientType::kTest1, ""); + NotificationEntryToProto(entry, &proto); + NotificationEntryFromProto(&proto, &expected); + EXPECT_EQ(entry->notification_data, expected.notification_data); + EXPECT_EQ(entry->schedule_params, expected.schedule_params); + + EXPECT_EQ(*entry, expected) + << "Output: " << test::DebugString(entry) + << " \n Expected: " << test::DebugString(&expected); +} + +TEST(ProtoConversionTest, IconEntryFromProto) { + IconProto proto; + proto.set_uuid(kUuid); + proto.set_icon(kData); + IconEntry entry; + + IconEntryFromProto(&proto, &entry); + + // Verify entry data. + EXPECT_EQ(entry.uuid, kUuid); + EXPECT_EQ(entry.data, kData); +} + +TEST(ProtoConversionTest, IconEntryToProto) { + IconEntry entry; + entry.data = kData; + entry.uuid = kUuid; + IconProto proto; + + IconEntryToProto(&entry, &proto); + + // Verify proto data. + EXPECT_EQ(proto.icon(), kData); + EXPECT_EQ(proto.uuid(), kUuid); +} + +// Verifies client state proto conversion. +TEST(ProtoConversionTest, ClientStateProtoConversion) { + // Verify basic fields. + ClientState client_state; + test::ImpressionTestData test_data{ + SchedulerClientType::kTest1, 3, {}, base::nullopt}; + test::AddImpressionTestData(test_data, &client_state); + TestClientStateConversion(&client_state); + + // Verify suppression info. + base::Time last_trigger_time; + bool success = + base::Time::FromString("04/25/20 01:00:00 AM", &last_trigger_time); + DCHECK(success); + auto duration = base::TimeDelta::FromDays(7); + auto suppression = SuppressionInfo(last_trigger_time, duration); + suppression.recover_goal = 5; + client_state.suppression_info = std::move(suppression); + TestClientStateConversion(&client_state); +} + +// Verifies impression proto conversion. +TEST(ProtoConversionTest, ImpressionProtoConversion) { + ClientState client_state; + client_state.type = SchedulerClientType::kTest1; + base::Time create_time; + bool success = base::Time::FromString("03/25/19 00:00:00 AM", &create_time); + DCHECK(success); + Impression impression{create_time, + UserFeedback::kHelpful, + ImpressionResult::kPositive, + true /*integrated*/, + SchedulerTaskTime::kMorning, + kGuid, + SchedulerClientType::kTest1}; + client_state.impressions.emplace_back(impression); + TestClientStateConversion(&client_state); + + auto& first_impression = *client_state.impressions.begin(); + + // Verify all feedback types. + std::vector<UserFeedback> feedback_types{ + UserFeedback::kNoFeedback, UserFeedback::kHelpful, + UserFeedback::kNotHelpful, UserFeedback::kClick, + UserFeedback::kDismiss, UserFeedback::kIgnore}; + for (const auto feedback_type : feedback_types) { + first_impression.feedback = feedback_type; + TestClientStateConversion(&client_state); + } + + // Verify all impression result types. + std::vector<ImpressionResult> impression_results{ + ImpressionResult::kInvalid, ImpressionResult::kPositive, + ImpressionResult::kNegative, ImpressionResult::kNeutral}; + for (const auto impression_result : impression_results) { + first_impression.impression = impression_result; + TestClientStateConversion(&client_state); + } + + // Verify all scheduler task time types. + std::vector<SchedulerTaskTime> task_times{SchedulerTaskTime::kUnknown, + SchedulerTaskTime::kMorning, + SchedulerTaskTime::kEvening}; + for (const auto task_start_time : task_times) { + first_impression.task_start_time = task_start_time; + TestClientStateConversion(&client_state); + } +} + +// Verifies multiple impressions are serialized correctly. +TEST(ProtoConversionTest, MultipleImpressionConversion) { + ClientState client_state; + base::Time create_time; + bool success = base::Time::FromString("04/25/20 01:00:00 AM", &create_time); + DCHECK(success); + + Impression impression{create_time, UserFeedback::kHelpful, + ImpressionResult::kPositive, true, + SchedulerTaskTime::kMorning}; + Impression other_impression{create_time, UserFeedback::kNoFeedback, + ImpressionResult::kNegative, false, + SchedulerTaskTime::kEvening}; + client_state.impressions.emplace_back(std::move(impression)); + client_state.impressions.emplace_back(std::move(other_impression)); + TestClientStateConversion(&client_state); +} + +// Verifies notification entry proto conversion. +TEST(ProtoConversionTest, NotificationEntryConversion) { + NotificationEntry entry(SchedulerClientType::kTest2, kGuid); + bool success = + base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time); + DCHECK(success); + TestNotificationEntryConversion(&entry); + + // Test notification data. + entry.notification_data.id = kGuid; + entry.notification_data.title = "title"; + entry.notification_data.message = "message"; + entry.notification_data.icon_uuid = "icon_uuid"; + entry.notification_data.url = "url"; + TestNotificationEntryConversion(&entry); + + // Test scheduling params. + const ScheduleParams::Priority priorities[] = { + ScheduleParams::Priority::kLow, ScheduleParams::Priority::kHigh, + ScheduleParams::Priority::kNoThrottle}; + for (auto priority : priorities) { + entry.schedule_params.priority = priority; + TestNotificationEntryConversion(&entry); + } +} + +} // namespace +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc new file mode 100644 index 0000000..bae20ee6 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.cc
@@ -0,0 +1,138 @@ +// Copyright 2019 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/notifications/scheduler/internal/scheduled_notification_manager.h" + +#include <algorithm> +#include <map> + +#include "base/bind.h" +#include "base/guid.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/notifications/scheduler/internal/notification_entry.h" +#include "chrome/browser/notifications/scheduler/public/notification_params.h" + +namespace notifications { +namespace { + +// Comparator used to sort notification entries based on creation time. +bool CreateTimeCompare(const NotificationEntry* lhs, + const NotificationEntry* rhs) { + DCHECK(lhs && rhs); + return lhs->create_time <= rhs->create_time; +} + +class ScheduledNotificationManagerImpl : public ScheduledNotificationManager { + public: + using Store = std::unique_ptr<CollectionStore<NotificationEntry>>; + + ScheduledNotificationManagerImpl(Store store) + : store_(std::move(store)), delegate_(nullptr), weak_ptr_factory_(this) {} + + private: + void Init(Delegate* delegate, InitCallback callback) override { + DCHECK(!delegate_); + delegate_ = delegate; + store_->InitAndLoad( + base::BindOnce(&ScheduledNotificationManagerImpl::OnStoreInitialized, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); + } + + // NotificationManager implementation. + void ScheduleNotification( + std::unique_ptr<NotificationParams> notification_params) override { + DCHECK(notification_params); + std::string guid = notification_params->guid; + DCHECK(!guid.empty()); + if (notifications_.find(guid) != notifications_.end()) { + // TODO(xingliu): Report duplicate guid failure. + return; + } + + auto entry = + std::make_unique<NotificationEntry>(notification_params->type, guid); + entry->notification_data = + std::move(notification_params->notification_data); + entry->schedule_params = std::move(notification_params->schedule_params); + auto* entry_ptr = entry.get(); + notifications_.emplace(guid, std::move(entry)); + store_->Add( + guid, *entry_ptr, + base::BindOnce(&ScheduledNotificationManagerImpl::OnNotificationAdded, + weak_ptr_factory_.GetWeakPtr())); + } + + void DisplayNotification(const std::string& guid) override { + auto it = notifications_.find(guid); + if (it == notifications_.end()) + return; + + // Move the entry to delegate, and delete it from the storage. + auto notification_entry = std::move(it->second); + notifications_.erase(guid); + store_->Delete( + guid, + base::BindOnce(&ScheduledNotificationManagerImpl::OnNotificationDeleted, + weak_ptr_factory_.GetWeakPtr())); + if (delegate_) + delegate_->DisplayNotification(std::move(notification_entry)); + } + + void GetAllNotifications(Notifications* notifications) override { + DCHECK(notifications); + + for (const auto& pair : notifications_) { + const auto& notif = pair.second; + DCHECK(notif); + (*notifications)[notif->type].emplace_back(notif.get()); + } + + // Sort by creation time for each notification type. + for (auto it = notifications->begin(); it != notifications->end(); ++it) { + std::sort(it->second.begin(), it->second.end(), &CreateTimeCompare); + } + } + + void OnStoreInitialized(InitCallback callback, + bool success, + CollectionStore<NotificationEntry>::Entries entries) { + if (!success) { + std::move(callback).Run(false); + return; + } + + for (auto it = entries.begin(); it != entries.end(); ++it) { + std::string guid = (*it)->guid; + notifications_.emplace(guid, std::move(*it)); + } + + std::move(callback).Run(true); + } + + void OnNotificationAdded(bool success) { NOTIMPLEMENTED(); } + + void OnNotificationDeleted(bool success) { NOTIMPLEMENTED(); } + + Store store_; + Delegate* delegate_; + std::map<std::string, std::unique_ptr<NotificationEntry>> notifications_; + + base::WeakPtrFactory<ScheduledNotificationManagerImpl> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManagerImpl); +}; + +} // namespace + +// static +std::unique_ptr<ScheduledNotificationManager> +ScheduledNotificationManager::Create( + std::unique_ptr<CollectionStore<NotificationEntry>> store) { + return std::make_unique<ScheduledNotificationManagerImpl>(std::move(store)); +} + +ScheduledNotificationManager::ScheduledNotificationManager() = default; + +ScheduledNotificationManager::~ScheduledNotificationManager() = default; + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h new file mode 100644 index 0000000..6ed3e8bc --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h
@@ -0,0 +1,73 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULED_NOTIFICATION_MANAGER_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULED_NOTIFICATION_MANAGER_H_ + +#include <map> +#include <memory> +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "chrome/browser/notifications/scheduler/internal/collection_store.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" + +namespace notifications { + +struct NotificationEntry; +struct NotificationParams; + +// Class to manage in-memory scheduled notifications loaded from the storage. +class ScheduledNotificationManager { + public: + using InitCallback = base::OnceCallback<void(bool)>; + using Notifications = + std::map<SchedulerClientType, std::vector<const NotificationEntry*>>; + + // Delegate that receives events from the manager. + class Delegate { + public: + // Displays a notification to the user. + virtual void DisplayNotification( + std::unique_ptr<NotificationEntry> notification) = 0; + + Delegate() = default; + virtual ~Delegate() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(Delegate); + }; + + // Creates the instance. + static std::unique_ptr<ScheduledNotificationManager> Create( + std::unique_ptr<CollectionStore<NotificationEntry>> store); + + // Initializes the notification store. + virtual void Init(Delegate* delegate, InitCallback callback) = 0; + + // Adds a new notification. + virtual void ScheduleNotification( + std::unique_ptr<NotificationParams> notification_params) = 0; + + // Displays a notification, the scheduled notification will be removed from + // storage, then Delegate::DisplayNotification() should be invoked. + virtual void DisplayNotification(const std::string& guid) = 0; + + // Gets all scheduled notifications. For each type, notifications are sorted + // by creation timestamp. + virtual void GetAllNotifications(Notifications* notifications) = 0; + + virtual ~ScheduledNotificationManager(); + + protected: + ScheduledNotificationManager(); + + private: + DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManager); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULED_NOTIFICATION_MANAGER_H_
diff --git a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc new file mode 100644 index 0000000..b5b1c132 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc
@@ -0,0 +1,229 @@ +// Copyright 2019 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/notifications/scheduler/internal/scheduled_notification_manager.h" + +#include "base/bind.h" +#include "base/guid.h" +#include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" +#include "chrome/browser/notifications/scheduler/internal/collection_store.h" +#include "chrome/browser/notifications/scheduler/internal/notification_entry.h" +#include "chrome/browser/notifications/scheduler/public/notification_params.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using ::testing::Invoke; +using Entries = std::vector<std::unique_ptr<notifications::NotificationEntry>>; + +namespace notifications { +namespace { + +const char kGuid[] = "test_guid_1234"; +const char kTitle[] = "test_title"; + +NotificationEntry CreateNotificationEntry() { + return NotificationEntry(SchedulerClientType::kUnknown, base::GenerateGUID()); +} + +class MockDelegate : public ScheduledNotificationManager::Delegate { + public: + MockDelegate() = default; + MOCK_METHOD1(DisplayNotification, void(std::unique_ptr<NotificationEntry>)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockDelegate); +}; + +class MockNotificationStore : public CollectionStore<NotificationEntry> { + public: + MockNotificationStore() {} + + MOCK_METHOD1(InitAndLoad, + void(CollectionStore<NotificationEntry>::LoadCallback)); + MOCK_METHOD3(Add, + void(const std::string&, + const NotificationEntry&, + base::OnceCallback<void(bool)>)); + MOCK_METHOD3(Update, + void(const std::string&, + const NotificationEntry&, + base::OnceCallback<void(bool)>)); + MOCK_METHOD2(Delete, + void(const std::string&, base::OnceCallback<void(bool)>)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockNotificationStore); +}; + +class ScheduledNotificationManagerTest : public testing::Test { + public: + ScheduledNotificationManagerTest() : store_(nullptr) {} + ~ScheduledNotificationManagerTest() override = default; + + void SetUp() override { + delegate_ = std::make_unique<MockDelegate>(); + auto store = std::make_unique<MockNotificationStore>(); + store_ = store.get(); + manager_ = ScheduledNotificationManager::Create(std::move(store)); + } + + protected: + ScheduledNotificationManager* manager() { return manager_.get(); } + MockNotificationStore* store() { return store_; } + MockDelegate* delegate() { return delegate_.get(); } + + // Initializes the manager with predefined data in the store. + void InitWithData(std::vector<NotificationEntry> data) { + Entries entries; + for (auto it = data.begin(); it != data.end(); ++it) { + auto entry_ptr = std::make_unique<NotificationEntry>( + SchedulerClientType::kUnknown, it->guid); + *(entry_ptr.get()) = *it; + entries.emplace_back(std::move(entry_ptr)); + } + + // Initialize the store and call the callback. + EXPECT_CALL(*store(), InitAndLoad(_)) + .WillOnce( + Invoke([&entries](base::OnceCallback<void(bool, Entries)> cb) { + std::move(cb).Run(true, std::move(entries)); + })); + base::RunLoop loop; + manager()->Init(delegate(), + base::BindOnce( + [](base::RepeatingClosure closure, bool success) { + EXPECT_TRUE(success); + std::move(closure).Run(); + }, + loop.QuitClosure())); + loop.Run(); + } + + private: + base::test::ScopedTaskEnvironment scoped_task_environment_; + std::unique_ptr<MockDelegate> delegate_; + MockNotificationStore* store_; + std::unique_ptr<ScheduledNotificationManager> manager_; + DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManagerTest); +}; + +// Verify that error is received when initialization failed. +TEST_F(ScheduledNotificationManagerTest, InitFailed) { + EXPECT_CALL(*store(), InitAndLoad(_)) + .WillOnce(Invoke([](base::OnceCallback<void(bool, Entries)> cb) { + std::move(cb).Run(false, Entries()); + })); + + base::RunLoop loop; + manager()->Init(delegate(), + base::BindOnce( + [](base::RepeatingClosure closure, bool success) { + // Expected to receive error. + EXPECT_FALSE(success); + std::move(closure).Run(); + }, + loop.QuitClosure())); + loop.Run(); +} + +// Test to schedule a notification. +TEST_F(ScheduledNotificationManagerTest, ScheduleNotification) { + InitWithData(std::vector<NotificationEntry>()); + NotificationData notification_data; + notification_data.title = kTitle; + ScheduleParams schedule_params; + schedule_params.priority = ScheduleParams::Priority::kHigh; + auto params = std::make_unique<NotificationParams>( + SchedulerClientType::kUnknown, notification_data, schedule_params); + std::string guid = params->guid; + EXPECT_FALSE(guid.empty()); + + // Verify call contract. + EXPECT_CALL(*store(), Add(guid, _, _)); + manager()->ScheduleNotification(std::move(params)); + + // Verify in-memory data. + ScheduledNotificationManager::Notifications notifications; + manager()->GetAllNotifications(¬ifications); + EXPECT_EQ(notifications.size(), 1u); + const NotificationEntry* entry = *(notifications.begin()->second.begin()); + EXPECT_EQ(entry->guid, guid); + EXPECT_NE(entry->create_time, base::Time()); + + // TODO(xingliu): change these to compare with operator==. + EXPECT_EQ(entry->notification_data.title, kTitle); + EXPECT_EQ(entry->schedule_params.priority, ScheduleParams::Priority::kHigh); +} + +// Test to schedule a notification without guid, we will auto generated one. +TEST_F(ScheduledNotificationManagerTest, ScheduleNotificationEmptyGuid) { + InitWithData(std::vector<NotificationEntry>()); + auto params = std::make_unique<NotificationParams>( + SchedulerClientType::kUnknown, NotificationData(), ScheduleParams()); + + // Verify call contract. + EXPECT_CALL(*store(), Add(_, _, _)); + manager()->ScheduleNotification(std::move(params)); + + // Verify in-memory data. + ScheduledNotificationManager::Notifications notifications; + manager()->GetAllNotifications(¬ifications); + EXPECT_EQ(notifications.size(), 1u); + const NotificationEntry* entry = *(notifications.begin()->second.begin()); + EXPECT_NE(entry->guid, std::string()); + EXPECT_NE(entry->create_time, base::Time()); +} + +// TODO(xingliu): change this to compare with operator==. +MATCHER_P(NotificationEntryIs, expected, "") { + return arg->guid == expected.guid; +} + +// Test to display a notification. +TEST_F(ScheduledNotificationManagerTest, DisplayNotification) { + auto entry = CreateNotificationEntry(); + entry.guid = kGuid; + InitWithData(std::vector<NotificationEntry>({entry})); + + // Verify delegate and dependency call contract. + EXPECT_CALL(*store(), Delete(kGuid, _)); + EXPECT_CALL(*delegate(), DisplayNotification(NotificationEntryIs(entry))); + manager()->DisplayNotification(kGuid); + + // Verify in-memory data. + ScheduledNotificationManager::Notifications notifications; + manager()->GetAllNotifications(¬ifications); + EXPECT_TRUE(notifications.empty()); +} + +// Verify GetAllNotifications API, the notification should be sorted based on +// creation timestamp. +TEST_F(ScheduledNotificationManagerTest, GetAllNotifications) { + // Ordering: entry1.create_time < entry0.create_time < entry2.create_time. + auto now = base::Time::Now(); + auto entry0 = CreateNotificationEntry(); + entry0.create_time = now; + auto entry1 = CreateNotificationEntry(); + entry1.create_time = now - base::TimeDelta::FromMinutes(1); + auto entry2 = CreateNotificationEntry(); + entry2.create_time = now + base::TimeDelta::FromMinutes(1); + + InitWithData(std::vector<NotificationEntry>({entry0, entry1, entry2})); + ScheduledNotificationManager::Notifications notifications; + manager()->GetAllNotifications(¬ifications); + EXPECT_EQ(notifications.size(), 1u); + EXPECT_EQ(notifications.begin()->second.size(), 3u); + + // Entries returned by GetAllNotifications() should be sorted by creation + // timestamp. + const NotificationEntry* output0 = notifications.begin()->second.front(); + const NotificationEntry* output2 = notifications.begin()->second.back(); + EXPECT_EQ(output0->create_time, entry1.create_time); + EXPECT_EQ(output2->create_time, entry2.create_time); +} + +} // namespace +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_config.cc b/chrome/browser/notifications/scheduler/internal/scheduler_config.cc new file mode 100644 index 0000000..b9a93d8 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/scheduler_config.cc
@@ -0,0 +1,51 @@ +// Copyright 2019 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/notifications/scheduler/internal/scheduler_config.h" + +namespace notifications { + +// The impression history is hold for 4 weeks. +constexpr base::TimeDelta kDefaultImpressionExpiration = + base::TimeDelta::FromDays(28); + +// The suppression lasts 8 weeks. +constexpr base::TimeDelta kDefaultSuppressionDuration = + base::TimeDelta::FromDays(56); + +// The morning task by default will run at 7am. +constexpr int kDefaultMorningTaskHour = 7; + +// The evening task by default will run at 6pm. +constexpr int kDefaultEveningTaskHour = 18; + +// Check consecutive notification dismisses in this duration to generate a +// dismiss event. +constexpr base::TimeDelta kDefaultDimissDuration = base::TimeDelta::FromDays(7); + +// Default background task time window duration. +constexpr base::TimeDelta kDefaultBackgroundTaskWindowDuration = + base::TimeDelta::FromHours(1); + +// static +std::unique_ptr<SchedulerConfig> SchedulerConfig::Create() { + return std::make_unique<SchedulerConfig>(); +} + +SchedulerConfig::SchedulerConfig() + : max_daily_shown_all_type(3), + max_daily_shown_per_type(10), + impression_expiration(kDefaultImpressionExpiration), + suppression_duration(kDefaultSuppressionDuration), + dismiss_count(3), + dismiss_duration(kDefaultDimissDuration), + morning_task_hour(kDefaultMorningTaskHour), + evening_task_hour(kDefaultEveningTaskHour), + background_task_window_duration(kDefaultBackgroundTaskWindowDuration) { + // TODO(xingliu): Add constructor using finch data. +} + +SchedulerConfig::~SchedulerConfig() = default; + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_config.h b/chrome/browser/notifications/scheduler/internal/scheduler_config.h new file mode 100644 index 0000000..617983e --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/scheduler_config.h
@@ -0,0 +1,61 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_CONFIG_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_CONFIG_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/time/time.h" + +namespace notifications { + +// Configuration of notification scheduler system. +struct SchedulerConfig { + // Creates a default scheduler config. + static std::unique_ptr<SchedulerConfig> Create(); + + SchedulerConfig(); + ~SchedulerConfig(); + + // Maximum number of all types of notifications shown to the user per day. + int max_daily_shown_all_type; + + // Maximum number of notifications shown to the user per day for each type. + int max_daily_shown_per_type; + + // The time for a notification impression history data to expire. The + // impression history will be deleted then. + base::TimeDelta impression_expiration; + + // Duration of suppression when negative impression is applied. + base::TimeDelta suppression_duration; + + // The number of consecutive notification dismisses to generate a dismiss + // event. + int dismiss_count; + + // Used to check whether |dismiss_count| consecutive notification dimisses are + // in this duration, to generate a dismiss event. + base::TimeDelta dismiss_duration; + + // The hour (from 0 to 23) to run the morning background task for notification + // scheduler. + int morning_task_hour; + + // The hour (from 0 to 23) to run the evening background task for notification + // scheduler. + int evening_task_hour; + + // The time window to launch the background task. + base::TimeDelta background_task_window_duration; + + private: + DISALLOW_COPY_AND_ASSIGN(SchedulerConfig); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_CONFIG_H_
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc b/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc new file mode 100644 index 0000000..ad7f8dd5 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/scheduler_utils.cc
@@ -0,0 +1,61 @@ +// Copyright 2019 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/notifications/scheduler/internal/scheduler_utils.h" + +#include "chrome/browser/notifications/scheduler/internal/impression_types.h" + +namespace notifications { + +bool ToLocalHour(int hour, + const base::Time& today, + int day_delta, + base::Time* out) { + DCHECK_GE(hour, 0); + DCHECK_LE(hour, 23); + DCHECK(out); + + // Gets the local time at |hour| in yesterday. + base::Time another_day = today + base::TimeDelta::FromDays(day_delta); + base::Time::Exploded another_day_exploded; + another_day.LocalExplode(&another_day_exploded); + another_day_exploded.hour = hour; + another_day_exploded.minute = 0; + another_day_exploded.second = 0; + another_day_exploded.millisecond = 0; + + // Converts local exploded time to time stamp. + return base::Time::FromLocalExploded(another_day_exploded, out); +} + +void NotificationsShownToday( + const std::map<SchedulerClientType, const ClientState*>& client_states, + std::map<SchedulerClientType, int>* shown_per_type, + int* shown_total, + SchedulerClientType* last_shown_type) { + base::Time last_shown_time; + base::Time now(base::Time::Now()); + base::Time beginning_of_today; + bool success = ToLocalHour(0, now, 0, &beginning_of_today); + DCHECK(success); + + for (const auto& state : client_states) { + const auto* client_state = state.second; + for (const auto& impression : client_state->impressions) { + // Tracks last notification shown to the user. + if (impression.create_time > last_shown_time) { + last_shown_time = impression.create_time; + *last_shown_type = client_state->type; + } + + // Count notification shown today. + if (impression.create_time >= beginning_of_today) { + (*shown_per_type)[client_state->type]++; + ++(*shown_total); + } + } + } +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_utils.h b/chrome/browser/notifications/scheduler/internal/scheduler_utils.h new file mode 100644 index 0000000..832a749 --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/scheduler_utils.h
@@ -0,0 +1,37 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_UTILS_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_UTILS_H_ + +#include <map> + +#include "base/time/time.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" + +namespace notifications { + +struct ClientState; + +// Retrieves the time stamp of a certain hour at a certain day from today. +// |hour| must be in the range of [0, 23]. +// |today| is a timestamp to define today, usually caller can directly pass in +// the current system time. +// |day_delta| is the different between the output date and today. +// Returns false if the conversion is failed. +bool ToLocalHour(int hour, + const base::Time& today, + int day_delta, + base::Time* out); + +// Calculates the notifications shown today from impression data. +void NotificationsShownToday( + const std::map<SchedulerClientType, const ClientState*>& client_states, + std::map<SchedulerClientType, int>* shown_per_type, + int* shown_total, + SchedulerClientType* last_shown_type); + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_INTERNAL_SCHEDULER_UTILS_H_
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_utils_unittest.cc b/chrome/browser/notifications/scheduler/internal/scheduler_utils_unittest.cc new file mode 100644 index 0000000..3c28e3a --- /dev/null +++ b/chrome/browser/notifications/scheduler/internal/scheduler_utils_unittest.cc
@@ -0,0 +1,42 @@ +// Copyright 2019 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/notifications/scheduler/internal/scheduler_utils.h" + +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace notifications { +namespace { + +// Verifies we can get the correct time stamp at certain hour in yesterday. +TEST(SchedulerUtilsTest, ToLocalHour) { + base::Time today, another_day, expected; + + // Timestamp of another day in the past. + EXPECT_TRUE(base::Time::FromString("10/15/07 12:45:12 PM", &today)); + EXPECT_TRUE(ToLocalHour(6, today, -1, &another_day)); + EXPECT_TRUE(base::Time::FromString("10/14/07 06:00:00 AM", &expected)); + EXPECT_EQ(expected, another_day); + + EXPECT_TRUE(base::Time::FromString("03/25/19 00:00:00 AM", &today)); + EXPECT_TRUE(ToLocalHour(0, today, -1, &another_day)); + EXPECT_TRUE(base::Time::FromString("03/24/19 00:00:00 AM", &expected)); + EXPECT_EQ(expected, another_day); + + // Timestamp of the same day. + EXPECT_TRUE(base::Time::FromString("03/25/19 00:00:00 AM", &today)); + EXPECT_TRUE(ToLocalHour(0, today, 0, &another_day)); + EXPECT_TRUE(base::Time::FromString("03/25/19 00:00:00 AM", &expected)); + EXPECT_EQ(expected, another_day); + + // Timestamp of another day in the future. + EXPECT_TRUE(base::Time::FromString("03/25/19 06:35:27 AM", &today)); + EXPECT_TRUE(ToLocalHour(16, today, 7, &another_day)); + EXPECT_TRUE(base::Time::FromString("04/01/19 16:00:00 PM", &expected)); + EXPECT_EQ(expected, another_day); +} + +} // namespace +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_background_task_scheduler.h b/chrome/browser/notifications/scheduler/notification_background_task_scheduler.h deleted file mode 100644 index 3c33cce5..0000000 --- a/chrome/browser/notifications/scheduler/notification_background_task_scheduler.h +++ /dev/null
@@ -1,60 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_BACKGROUND_TASK_SCHEDULER_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_BACKGROUND_TASK_SCHEDULER_H_ - -#include "base/macros.h" -#include "base/time/time.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" - -namespace notifications { - -// Interface to schedule a background task on platform OS to run the -// notification scheduler job. -class NotificationBackgroundTaskScheduler { - public: - // Interface used to handle background task events. - class Handler { - public: - // Called when the background task is started. - virtual void OnStartTask() = 0; - - // Called when the background task is stopped by the OS when it wants to - // reallocate resources, our task is not finished yet in this case. The - // handler implementation should explicitly decide whether the task should - // be rescheduled and run later. - virtual void OnStopTask() = 0; - - protected: - Handler() = default; - virtual ~Handler() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(Handler); - }; - - // Schedules a background task in a time window between |window_start| and - // |window_end|. This will update the current background task. Only one - // background task exists for notification scheduler. |scheduler_task_time| - // tag is passed to background task go support arbitrary time background task. - virtual void Schedule(notifications::SchedulerTaskTime scheduler_task_time, - base::TimeDelta window_start, - base::TimeDelta window_end) = 0; - - // Cancels the background task. - virtual void Cancel() = 0; - - virtual ~NotificationBackgroundTaskScheduler() = default; - - protected: - NotificationBackgroundTaskScheduler() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(NotificationBackgroundTaskScheduler); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_BACKGROUND_TASK_SCHEDULER_H_
diff --git a/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h b/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h index 94d7322..1586117c 100644 --- a/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h +++ b/chrome/browser/notifications/scheduler/notification_background_task_scheduler_android.h
@@ -7,7 +7,7 @@ #include "base/macros.h" #include "base/time/time.h" -#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h" +#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h" // This class contains: // 1. Android implementation of NotificationBackgroundTaskScheduler, which
diff --git a/chrome/browser/notifications/scheduler/notification_background_task_scheduler_impl.h b/chrome/browser/notifications/scheduler/notification_background_task_scheduler_impl.h index 13584e2..89a4e99 100644 --- a/chrome/browser/notifications/scheduler/notification_background_task_scheduler_impl.h +++ b/chrome/browser/notifications/scheduler/notification_background_task_scheduler_impl.h
@@ -7,7 +7,7 @@ #include "base/macros.h" #include "base/time/time.h" -#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h" +#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h" // Default implementation of NotificatioNBackgroundTaskScheduler. class NotificationBackgroundTaskSchedulerImpl
diff --git a/chrome/browser/notifications/scheduler/notification_data.cc b/chrome/browser/notifications/scheduler/notification_data.cc deleted file mode 100644 index 8e2e019..0000000 --- a/chrome/browser/notifications/scheduler/notification_data.cc +++ /dev/null
@@ -1,20 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/notification_data.h" - -namespace notifications { - -NotificationData::NotificationData() = default; - -NotificationData::NotificationData(const NotificationData& other) = default; - -bool NotificationData::operator==(const NotificationData& other) const { - return id == other.id && title == other.title && message == other.message && - icon_uuid == other.icon_uuid && url == other.url; -} - -NotificationData::~NotificationData() = default; - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_data.h b/chrome/browser/notifications/scheduler/notification_data.h deleted file mode 100644 index 2754899..0000000 --- a/chrome/browser/notifications/scheduler/notification_data.h +++ /dev/null
@@ -1,43 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_DATA_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_DATA_H_ - -#include <string> - -namespace notifications { - -// Contains data used to display a scheduled notification. All fields will be -// persisted to disk as protobuffer NotificationData. The clients of -// notification scheduler can optionally use the texts or icon in this struct, -// or use hard coded assets id. -struct NotificationData { - NotificationData(); - NotificationData(const NotificationData& other); - bool operator==(const NotificationData& other) const; - ~NotificationData(); - - // The unique identifier of the notification. This is not used as the key for - // the database entry, but the id of other notification struct plumbed to - // the scheduler system. - std::string id; - - // The title of the notification. - std::string title; - - // The body text of the notification. - std::string message; - - // The unique identifier of the icon, which must be loaded asynchronously into - // memory. - std::string icon_uuid; - - // URL of the web site responsible for showing the notification. - std::string url; -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_DATA_H_
diff --git a/chrome/browser/notifications/scheduler/notification_entry.cc b/chrome/browser/notifications/scheduler/notification_entry.cc deleted file mode 100644 index e1e7d31e..0000000 --- a/chrome/browser/notifications/scheduler/notification_entry.cc +++ /dev/null
@@ -1,29 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/notification_entry.h" - -#include <utility> - -namespace notifications { - -NotificationEntry::NotificationEntry() - : NotificationEntry(SchedulerClientType::kUnknown, std::string()) {} - -NotificationEntry::NotificationEntry(SchedulerClientType type, - const std::string& guid) - : type(type), guid(guid), create_time(base::Time::Now()) {} - -NotificationEntry::NotificationEntry(const NotificationEntry& other) = default; - -bool NotificationEntry::operator==(const NotificationEntry& other) const { - return type == other.type && guid == other.guid && - create_time == other.create_time && - notification_data == other.notification_data && - schedule_params == other.schedule_params; -} - -NotificationEntry::~NotificationEntry() = default; - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_entry.h b/chrome/browser/notifications/scheduler/notification_entry.h deleted file mode 100644 index 1ee0c7e2..0000000 --- a/chrome/browser/notifications/scheduler/notification_entry.h +++ /dev/null
@@ -1,44 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_ENTRY_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_ENTRY_H_ - -#include <string> - -#include "base/time/time.h" -#include "chrome/browser/notifications/scheduler/notification_data.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" -#include "chrome/browser/notifications/scheduler/schedule_params.h" - -namespace notifications { - -// Represents the in-memory counterpart of scheduled notification database -// record. -struct NotificationEntry { - NotificationEntry(); - NotificationEntry(SchedulerClientType type, const std::string& guid); - NotificationEntry(const NotificationEntry& other); - bool operator==(const NotificationEntry& other) const; - ~NotificationEntry(); - - // The type of the notification. - SchedulerClientType type; - - // The unique id of the notification database entry. - std::string guid; - - // Creation timestamp. - base::Time create_time; - - // Contains information to construct the notification. - NotificationData notification_data; - - // Scheduling details. - ScheduleParams schedule_params; -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_ENTRY_H_
diff --git a/chrome/browser/notifications/scheduler/notification_params.cc b/chrome/browser/notifications/scheduler/notification_params.cc deleted file mode 100644 index 8cf8ab81..0000000 --- a/chrome/browser/notifications/scheduler/notification_params.cc +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/notification_params.h" - -#include <utility> - -#include "base/guid.h" -#include "chrome/browser/notifications/scheduler/schedule_params.h" - -namespace notifications { - -NotificationParams::NotificationParams(SchedulerClientType type, - NotificationData notification_data, - ScheduleParams schedule_params) - : type(type), - guid(base::GenerateGUID()), - notification_data(std::move(notification_data)), - schedule_params(std::move(schedule_params)) {} - -NotificationParams::~NotificationParams() = default; - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_params.h b/chrome/browser/notifications/scheduler/notification_params.h deleted file mode 100644 index 6bfabf7..0000000 --- a/chrome/browser/notifications/scheduler/notification_params.h +++ /dev/null
@@ -1,39 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_PARAMS_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_PARAMS_H_ - -#include <memory> - -#include "chrome/browser/notifications/scheduler/notification_data.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" -#include "chrome/browser/notifications/scheduler/schedule_params.h" - -namespace notifications { - -// Struct used to schedule a notification. -struct NotificationParams { - NotificationParams(SchedulerClientType type, - NotificationData notification, - ScheduleParams schedule_params); - ~NotificationParams(); - - // The type of notification using the scheduling system. - SchedulerClientType type; - - // An auto generated unique id of the scheduled notification. - std::string guid; - - // Data used to show the notification, such as text or title on the - // notification. - NotificationData notification_data; - - // Scheduling details used to determine when to show the notification. - ScheduleParams schedule_params; -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_PARAMS_H_
diff --git a/chrome/browser/notifications/scheduler/notification_schedule_service.h b/chrome/browser/notifications/scheduler/notification_schedule_service.h deleted file mode 100644 index 1e6091f..0000000 --- a/chrome/browser/notifications/scheduler/notification_schedule_service.h +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULE_SERVICE_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULE_SERVICE_H_ - -#include <memory> - -#include "base/macros.h" -#include "components/keyed_service/core/keyed_service.h" - -namespace notifications { - -struct NotificationParams; - -// Service to schedule a notification to display in the future. An internal -// throttling mechanism will be applied to limit the maximum notification shown -// to the user. Also user's interaction with the notification will affect the -// frequency of notification delivery. -class NotificationScheduleService : public KeyedService { - public: - // Schedules a notification to display. - virtual void Schedule( - std::unique_ptr<NotificationParams> notification_params) = 0; - - protected: - NotificationScheduleService() = default; - ~NotificationScheduleService() override = default; - - private: - DISALLOW_COPY_AND_ASSIGN(NotificationScheduleService); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULE_SERVICE_H_
diff --git a/chrome/browser/notifications/scheduler/notification_schedule_service_factory.cc b/chrome/browser/notifications/scheduler/notification_schedule_service_factory.cc index b884da0..899d980852 100644 --- a/chrome/browser/notifications/scheduler/notification_schedule_service_factory.cc +++ b/chrome/browser/notifications/scheduler/notification_schedule_service_factory.cc
@@ -8,9 +8,8 @@ #include <utility> #include "chrome/browser/notifications/scheduler/notification_background_task_scheduler_impl.h" -#include "chrome/browser/notifications/scheduler/notification_schedule_service.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_context.h" +#include "chrome/browser/notifications/scheduler/public/notification_schedule_service.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h" #include "chrome/browser/notifications/scheduler/schedule_service_factory_helper.h" #include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/notifications/scheduler/notification_schedule_service_impl.cc b/chrome/browser/notifications/scheduler/notification_schedule_service_impl.cc deleted file mode 100644 index f9565982..0000000 --- a/chrome/browser/notifications/scheduler/notification_schedule_service_impl.cc +++ /dev/null
@@ -1,26 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/notification_schedule_service_impl.h" - -#include <utility> - -#include "base/logging.h" -#include "chrome/browser/notifications/scheduler/notification_params.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler.h" - -namespace notifications { - -NotificationScheduleServiceImpl::NotificationScheduleServiceImpl( - std::unique_ptr<NotificationScheduler> scheduler) - : scheduler_(std::move(scheduler)) {} - -NotificationScheduleServiceImpl::~NotificationScheduleServiceImpl() = default; - -void NotificationScheduleServiceImpl::Schedule( - std::unique_ptr<NotificationParams> notification_params) { - scheduler_->Schedule(std::move(notification_params)); -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_schedule_service_impl.h b/chrome/browser/notifications/scheduler/notification_schedule_service_impl.h deleted file mode 100644 index d739bf4..0000000 --- a/chrome/browser/notifications/scheduler/notification_schedule_service_impl.h +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULE_SERVICE_IMPL_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULE_SERVICE_IMPL_H_ - -#include <memory> - -#include "base/macros.h" -#include "chrome/browser/notifications/scheduler/notification_schedule_service.h" - -namespace notifications { - -class NotificationScheduler; -struct NotificationParams; - -class NotificationScheduleServiceImpl : public NotificationScheduleService { - public: - explicit NotificationScheduleServiceImpl( - std::unique_ptr<NotificationScheduler> scheduler); - ~NotificationScheduleServiceImpl() override; - - private: - // NotificationScheduleService implementation. - void Schedule( - std::unique_ptr<NotificationParams> notification_params) override; - - // Provides the actual notification scheduling functionalities. - std::unique_ptr<NotificationScheduler> scheduler_; - - DISALLOW_COPY_AND_ASSIGN(NotificationScheduleServiceImpl); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULE_SERVICE_IMPL_H_
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler.cc b/chrome/browser/notifications/scheduler/notification_scheduler.cc deleted file mode 100644 index 4c84d8bd..0000000 --- a/chrome/browser/notifications/scheduler/notification_scheduler.cc +++ /dev/null
@@ -1,226 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/notification_scheduler.h" - -#include <string> -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/logging.h" -#include "base/memory/weak_ptr.h" -#include "base/optional.h" -#include "chrome/browser/notifications/scheduler/display_decider.h" -#include "chrome/browser/notifications/scheduler/distribution_policy.h" -#include "chrome/browser/notifications/scheduler/icon_store.h" -#include "chrome/browser/notifications/scheduler/impression_history_tracker.h" -#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h" -#include "chrome/browser/notifications/scheduler/notification_entry.h" -#include "chrome/browser/notifications/scheduler/notification_params.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_client.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_context.h" -#include "chrome/browser/notifications/scheduler/scheduled_notification_manager.h" -#include "chrome/browser/notifications/scheduler/user_action_handler.h" - -namespace notifications { -namespace { - -class NotificationSchedulerImpl; - -// Helper class to do async initialization in parallel for multiple subsystem -// instances. -class InitHelper { - public: - using InitCallback = base::OnceCallback<void(bool)>; - InitHelper() : weak_ptr_factory_(this) {} - - ~InitHelper() = default; - - // Initializes subsystems in notification scheduler, |callback| will be - // invoked if all initializations finished or anyone of them failed. The - // object should be destroyed along with the |callback|. - void Init( - NotificationSchedulerContext* context, - ScheduledNotificationManager::Delegate* notification_manager_delegate, - ImpressionHistoryTracker::Delegate* impression_tracker_delegate, - InitCallback callback) { - callback_ = std::move(callback); - context->icon_store()->Init(base::BindOnce( - &InitHelper::OnIconStoreInitialized, weak_ptr_factory_.GetWeakPtr())); - context->impression_tracker()->Init( - impression_tracker_delegate, - base::BindOnce(&InitHelper::OnImpressionTrackerInitialized, - weak_ptr_factory_.GetWeakPtr())); - context->notification_manager()->Init( - notification_manager_delegate, - base::BindOnce(&InitHelper::OnNotificationManagerInitialized, - weak_ptr_factory_.GetWeakPtr())); - } - - private: - void OnIconStoreInitialized(bool success) { - icon_store_initialzed_ = success; - MaybeFinishInitialization(); - } - - void OnImpressionTrackerInitialized(bool success) { - impression_tracker_initialzed_ = success; - MaybeFinishInitialization(); - } - - void OnNotificationManagerInitialized(bool success) { - notification_manager_initialized_ = success; - MaybeFinishInitialization(); - } - - void MaybeFinishInitialization() { - bool all_finished = icon_store_initialzed_.has_value() && - impression_tracker_initialzed_.has_value() && - notification_manager_initialized_.has_value(); - // Notify the initialization result when all subcomponents are initialized. - if (!all_finished) - return; - - bool success = icon_store_initialzed_.value() && - impression_tracker_initialzed_.value() && - notification_manager_initialized_.value(); - std::move(callback_).Run(success); - } - - InitCallback callback_; - base::Optional<bool> icon_store_initialzed_; - base::Optional<bool> impression_tracker_initialzed_; - base::Optional<bool> notification_manager_initialized_; - - base::WeakPtrFactory<InitHelper> weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(InitHelper); -}; - -// Implementation of NotificationScheduler. -class NotificationSchedulerImpl - : public NotificationScheduler, - public NotificationBackgroundTaskScheduler::Handler, - public ScheduledNotificationManager::Delegate, - public ImpressionHistoryTracker::Delegate, - public UserActionHandler { - public: - NotificationSchedulerImpl( - std::unique_ptr<NotificationSchedulerContext> context) - : context_(std::move(context)), weak_ptr_factory_(this) {} - - ~NotificationSchedulerImpl() override = default; - - private: - // NotificationScheduler implementation. - void Init(InitCallback init_callback) override { - auto helper = std::make_unique<InitHelper>(); - auto* helper_ptr = helper.get(); - helper_ptr->Init( - context_.get(), this, this, - base::BindOnce(&NotificationSchedulerImpl::OnInitialized, - weak_ptr_factory_.GetWeakPtr(), std::move(helper), - std::move(init_callback))); - } - - void Schedule( - std::unique_ptr<NotificationParams> notification_params) override { - context_->notification_manager()->ScheduleNotification( - std::move(notification_params)); - } - - void OnInitialized(std::unique_ptr<InitHelper>, - InitCallback init_callback, - bool success) { - // TODO(xingliu): Inform the clients about initialization results and tear - // down internal components. - std::move(init_callback).Run(success); - } - - // NotificationBackgroundTaskScheduler::Handler implementation. - void OnStartTask() override { - // Updates the impression data to compute daily notification shown budget. - context_->impression_tracker()->AnalyzeImpressionHistory(); - - // TODO(xingliu): Pass SchedulerTaskTime from background task. - FindNotificationToShow(SchedulerTaskTime::kMorning); - - // Schedule the next background task based on scheduled notifications. - ScheduleBackgroundTask(); - } - - void OnStopTask() override { ScheduleBackgroundTask(); } - - // ScheduledNotificationManager::Delegate implementation. - void DisplayNotification( - std::unique_ptr<NotificationEntry> notification) override { - // TODO(xingliu): Inform the clients and show the notification. - NOTIMPLEMENTED(); - } - - // ImpressionHistoryTracker::Delegate implementation. - void OnImpressionUpdated() override { ScheduleBackgroundTask(); } - - void FindNotificationToShow(SchedulerTaskTime task_start_time) { - DisplayDecider::Results results; - ScheduledNotificationManager::Notifications notifications; - context_->notification_manager()->GetAllNotifications(¬ifications); - - DisplayDecider::ClientStates client_states; - context_->impression_tracker()->GetClientStates(&client_states); - - std::vector<SchedulerClientType> clients; - context_->client_registrar()->GetRegisteredClients(&clients); - - context_->display_decider()->FindNotificationsToShow( - context_->config(), std::move(clients), DistributionPolicy::Create(), - task_start_time, std::move(notifications), std::move(client_states), - &results); - - // TODO(xingliu): Update impression data after notification shown. - // See https://crbug.com/965133. - for (const auto& guid : results) { - context_->notification_manager()->DisplayNotification(guid); - } - } - - void ScheduleBackgroundTask() { - // TODO(xingliu): Implements a class to determine the next background task - // based on scheduled notification data. - NOTIMPLEMENTED(); - } - - void OnClick(const std::string& notification_id) override { - context_->impression_tracker()->OnClick(notification_id); - } - - void OnActionClick(const std::string& notification_id, - ActionButtonType button_type) override { - context_->impression_tracker()->OnActionClick(notification_id, button_type); - } - - void OnDismiss(const std::string& notification_id) override { - context_->impression_tracker()->OnDismiss(notification_id); - } - - std::unique_ptr<NotificationSchedulerContext> context_; - - base::WeakPtrFactory<NotificationSchedulerImpl> weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerImpl); -}; - -} // namespace - -// static -std::unique_ptr<NotificationScheduler> NotificationScheduler::Create( - std::unique_ptr<NotificationSchedulerContext> context) { - return std::make_unique<NotificationSchedulerImpl>(std::move(context)); -} - -NotificationScheduler::NotificationScheduler() = default; - -NotificationScheduler::~NotificationScheduler() = default; - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler.h b/chrome/browser/notifications/scheduler/notification_scheduler.h deleted file mode 100644 index 7b3eea2..0000000 --- a/chrome/browser/notifications/scheduler/notification_scheduler.h +++ /dev/null
@@ -1,43 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_H_ - -#include <memory> - -#include "base/callback.h" -#include "base/macros.h" - -namespace notifications { - -class NotificationSchedulerContext; -struct NotificationParams; - -// Provides notification scheduling and throttling functionalities. This class -// glues all the subsystems together for notification scheduling system. -class NotificationScheduler { - public: - using InitCallback = base::OnceCallback<void(bool)>; - static std::unique_ptr<NotificationScheduler> Create( - std::unique_ptr<NotificationSchedulerContext> context); - - NotificationScheduler(); - virtual ~NotificationScheduler(); - - // Initializes the scheduler. - virtual void Init(InitCallback init_callback) = 0; - - // Schedules a notification to show in the future. Throttling logic may apply - // based on |notification_params|. - virtual void Schedule( - std::unique_ptr<NotificationParams> notification_params) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(NotificationScheduler); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_H_
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_client.h b/chrome/browser/notifications/scheduler/notification_scheduler_client.h deleted file mode 100644 index 1272f6e..0000000 --- a/chrome/browser/notifications/scheduler/notification_scheduler_client.h +++ /dev/null
@@ -1,77 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CLIENT_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CLIENT_H_ - -#include <map> -#include <memory> -#include <set> -#include <string> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/optional.h" -#include "chrome/browser/notifications/scheduler/notification_data.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" -#include "third_party/skia/include/core/SkBitmap.h" - -namespace notifications { - -// The client interface to receive events from notification scheduler. -class NotificationSchedulerClient { - public: - struct DisplayData { - NotificationData notification_data; - SkBitmap icon; - }; - - // Defines user actions type. - enum class UserActionType { - // The user clicks on the notification body. - kClick = 0, - // The user clicks on the notification button. - kButtonClick = 1, - // The user dismisses the notification. - kDismiss = 2, - }; - - // Information about button clicks. - struct ButtonClickInfo { - // Unique id of the button. - std::string button_id; - - // Associate impression type for the button. - ActionButtonType type = ActionButtonType::kUnknownAction; - }; - - using DisplayCallback = - base::OnceCallback<void(std::unique_ptr<DisplayData>)>; - - NotificationSchedulerClient(); - virtual ~NotificationSchedulerClient(); - - // Called when the notification should be displayed to the user. The clients - // can overwrite data in |display_data| and return the updated data in - // |callback|. - virtual void ShowNotification(std::unique_ptr<DisplayData> display_data, - DisplayCallback callback) = 0; - - // Called when scheduler is initialized, number of notification scheduled for - // this type is reported if initialization succeeded. - virtual void OnSchedulerInitialized(bool success, - std::set<std::string> guids) = 0; - - // Called when the user interacts with the notification. - virtual void OnUserAction(UserActionType action_type, - const std::string& notification_id, - base::Optional<ButtonClickInfo> button_info) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerClient); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CLIENT_H_
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.cc b/chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.cc deleted file mode 100644 index f753f7d..0000000 --- a/chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.cc +++ /dev/null
@@ -1,44 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/notification_scheduler_client_registrar.h" - -#include <utility> - -#include "base/logging.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_client.h" - -namespace notifications { - -NotificationSchedulerClientRegistrar::NotificationSchedulerClientRegistrar() = - default; - -NotificationSchedulerClientRegistrar::~NotificationSchedulerClientRegistrar() = - default; - -void NotificationSchedulerClientRegistrar::RegisterClient( - SchedulerClientType type, - std::unique_ptr<NotificationSchedulerClient> client) { - DCHECK(clients_.find(type) == clients_.end()); - clients_.emplace(type, std::move(client)); -} - -NotificationSchedulerClient* NotificationSchedulerClientRegistrar::GetClient( - SchedulerClientType type) { - auto it = clients_.find(type); - if (it == clients_.end()) - return nullptr; - return it->second.get(); -} - -void NotificationSchedulerClientRegistrar::GetRegisteredClients( - std::vector<SchedulerClientType>* clients) const { - DCHECK(clients); - clients->clear(); - for (const auto& pair : clients_) { - clients->emplace_back(pair.first); - } -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h b/chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h deleted file mode 100644 index ed463cb..0000000 --- a/chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h +++ /dev/null
@@ -1,47 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CLIENT_REGISTRAR_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CLIENT_REGISTRAR_H_ - -#include <map> -#include <memory> -#include <vector> - -#include "base/macros.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" - -namespace notifications { - -class NotificationSchedulerClient; - -// Registers and maintains a list of NotificationSchedulerClient -// implementations. -class NotificationSchedulerClientRegistrar { - public: - NotificationSchedulerClientRegistrar(); - ~NotificationSchedulerClientRegistrar(); - - // Registers a client into notification scheduler system. - void RegisterClient(SchedulerClientType type, - std::unique_ptr<NotificationSchedulerClient> client); - - // Gets a NotificationSchedulerClient, nullptr if the type doesn't exist. - NotificationSchedulerClient* GetClient(SchedulerClientType type); - - // Gets a list of registered clients, sorted by integer value of - // SchedulerClientType. - void GetRegisteredClients(std::vector<SchedulerClientType>* clients) const; - - private: - using ClientsMap = std::map<SchedulerClientType, - std::unique_ptr<NotificationSchedulerClient>>; - ClientsMap clients_; - - DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerClientRegistrar); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CLIENT_REGISTRAR_H_
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_context.cc b/chrome/browser/notifications/scheduler/notification_scheduler_context.cc deleted file mode 100644 index 799dd68..0000000 --- a/chrome/browser/notifications/scheduler/notification_scheduler_context.cc +++ /dev/null
@@ -1,36 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/notification_scheduler_context.h" - -#include <utility> - -#include "chrome/browser/notifications/scheduler/display_decider.h" -#include "chrome/browser/notifications/scheduler/icon_store.h" -#include "chrome/browser/notifications/scheduler/impression_history_tracker.h" -#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h" -#include "chrome/browser/notifications/scheduler/scheduled_notification_manager.h" -#include "chrome/browser/notifications/scheduler/scheduler_config.h" - -namespace notifications { - -NotificationSchedulerContext::NotificationSchedulerContext( - std::unique_ptr<NotificationSchedulerClientRegistrar> client_registrar, - std::unique_ptr<NotificationBackgroundTaskScheduler> scheduler, - std::unique_ptr<IconStore> icon_store, - std::unique_ptr<ImpressionHistoryTracker> impression_tracker, - std::unique_ptr<ScheduledNotificationManager> notification_manager, - std::unique_ptr<DisplayDecider> display_decider, - std::unique_ptr<SchedulerConfig> config) - : client_registrar_(std::move(client_registrar)), - background_task_scheduler_(std::move(scheduler)), - impression_tracker_(std::move(impression_tracker)), - notification_manager_(std::move(notification_manager)), - display_decider_(std::move(display_decider)), - config_(std::move(config)) {} - -NotificationSchedulerContext::~NotificationSchedulerContext() = default; - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_context.h b/chrome/browser/notifications/scheduler/notification_scheduler_context.h deleted file mode 100644 index 87d2a52..0000000 --- a/chrome/browser/notifications/scheduler/notification_scheduler_context.h +++ /dev/null
@@ -1,88 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CONTEXT_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CONTEXT_H_ - -#include <memory> -#include <vector> - -#include "base/macros.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" - -namespace notifications { - -class DisplayDecider; -class IconStore; -class ImpressionHistoryTracker; -class NotificationBackgroundTaskScheduler; -class NotificationSchedulerClientRegistrar; -class ScheduledNotificationManager; -struct SchedulerConfig; - -// Context that contains necessary components needed by the notification -// scheduler to perform tasks. -class NotificationSchedulerContext { - public: - NotificationSchedulerContext( - std::unique_ptr<NotificationSchedulerClientRegistrar> client_registrar, - std::unique_ptr<NotificationBackgroundTaskScheduler> scheduler, - std::unique_ptr<IconStore> icon_store, - std::unique_ptr<ImpressionHistoryTracker> impression_tracker, - std::unique_ptr<ScheduledNotificationManager> notification_manager, - std::unique_ptr<DisplayDecider> display_decider, - std::unique_ptr<SchedulerConfig> config); - ~NotificationSchedulerContext(); - - NotificationSchedulerClientRegistrar* client_registrar() { - return client_registrar_.get(); - } - - NotificationBackgroundTaskScheduler* background_task_scheduler() { - return background_task_scheduler_.get(); - } - - IconStore* icon_store() { return icon_store_.get(); } - - ImpressionHistoryTracker* impression_tracker() { - return impression_tracker_.get(); - } - - ScheduledNotificationManager* notification_manager() { - return notification_manager_.get(); - } - - DisplayDecider* display_decider() { return display_decider_.get(); } - - const SchedulerConfig* config() const { return config_.get(); } - - private: - // Holds a list of clients using the notification scheduler system. - std::unique_ptr<NotificationSchedulerClientRegistrar> client_registrar_; - - // Used to schedule background task in OS level. - std::unique_ptr<NotificationBackgroundTaskScheduler> - background_task_scheduler_; - - // Stores notification icons. - std::unique_ptr<IconStore> icon_store_; - - // Tracks user impressions towards specific notification type. - std::unique_ptr<ImpressionHistoryTracker> impression_tracker_; - - // Stores all scheduled notifications. - std::unique_ptr<ScheduledNotificationManager> notification_manager_; - - // Helper class to decide which notification should be displayed to the user. - std::unique_ptr<DisplayDecider> display_decider_; - - // System configuration. - std::unique_ptr<SchedulerConfig> config_; - - DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerContext); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_CONTEXT_H_
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_types.h b/chrome/browser/notifications/scheduler/notification_scheduler_types.h deleted file mode 100644 index 2bb7488..0000000 --- a/chrome/browser/notifications/scheduler/notification_scheduler_types.h +++ /dev/null
@@ -1,84 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_TYPES_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_TYPES_H_ - -namespace notifications { - -// Enum to describe the time to process scheduled notification data. -// A Java counterpart will be generated for this enum. -// GENERATED_JAVA_ENUM_PACKAGE: ( -// org.chromium.chrome.browser.notifications.scheduler) -enum class SchedulerTaskTime { - // The system is started from normal user launch or other background - // tasks. - kUnknown = 0, - // Background task runs in the morning. - kMorning = 1, - // Background task runs in the evening. - kEvening = 2, -}; - -// The type of a list of clients using the notification scheduler system. -enum class SchedulerClientType { - // Test only values. - kTest1 = -1, - kTest2 = -2, - kTest3 = -3, - - // Default value of client type. - kUnknown = 0, - kMaxValue = kUnknown -}; - -// The type of user feedback from a displayed notification. -enum class UserFeedback { - // No user feedback yet. - kNoFeedback = 0, - // The user taps the helpful button, potentially a strong indicator of user's - // positive preference on the notification. - kHelpful = 1, - // The user taps the unhelpful button, potentially a strong indicator of - // user's negative preference on the notification. - kNotHelpful = 2, - // The user clicks the notification. - kClick = 3, - // The user clicks the body of the notification. - kDismiss = 4, - // The user has no interaction with the notification for a while. - kIgnore = 5, - kMaxValue = kIgnore -}; - -// The user impression of a particular notification, which may impact the -// notification display frenquency. -enum class ImpressionResult { - // Invalid user impression. - kInvalid = 0, - // Positive user impression that the user may like the notification. - kPositive = 1, - // Positive user impression that the user may dislike the notification. - kNegative = 2, - // The feedback is neutral to the user. - kNeutral = 3, - kMaxValue = kNeutral -}; - -// Categorizes type of notification buttons. Different type of button clicks -// may result in change of notification shown frequency. -enum class ActionButtonType { - // The action button is not categorized. - kUnknownAction = 0, - - // Helpful button indicates the user likes to interact with the notification. - kHelpful = 1, - - // Unhelpful button indicates dislike of the notification. - kUnhelpful = 2, -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_SCHEDULER_TYPES_H_
diff --git a/chrome/browser/notifications/scheduler/notification_store.cc b/chrome/browser/notifications/scheduler/notification_store.cc deleted file mode 100644 index 0bc756e..0000000 --- a/chrome/browser/notifications/scheduler/notification_store.cc +++ /dev/null
@@ -1,102 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/notification_store.h" - -#include "base/bind.h" -#include "chrome/browser/notifications/scheduler/proto_conversion.h" - -namespace leveldb_proto { -void DataToProto(notifications::NotificationEntry* entry, - notifications::proto::NotificationEntry* proto) { - NotificationEntryToProto(entry, proto); -} - -void ProtoToData(notifications::proto::NotificationEntry* proto, - notifications::NotificationEntry* entry) { - NotificationEntryFromProto(proto, entry); -} -} // namespace leveldb_proto - -namespace notifications { - -NotificationStore::NotificationStore( - std::unique_ptr<leveldb_proto::ProtoDatabase<proto::NotificationEntry, - NotificationEntry>> db) - : db_(std::move(db)), weak_ptr_factory_(this) {} - -NotificationStore::~NotificationStore() = default; - -void NotificationStore::InitAndLoad(LoadCallback callback) { - db_->Init(base::BindOnce(&NotificationStore::OnDbInitialized, - weak_ptr_factory_.GetWeakPtr(), - std::move(callback))); -} - -void NotificationStore::OnDbInitialized( - LoadCallback callback, - leveldb_proto::Enums::InitStatus status) { - if (status != leveldb_proto::Enums::InitStatus::kOK) { - std::move(callback).Run(false, Entries()); - return; - } - - // Load the data after a successful initialization. - db_->LoadEntries(base::BindOnce(&NotificationStore::OnDataLoaded, - weak_ptr_factory_.GetWeakPtr(), - std::move(callback))); -} - -void NotificationStore::OnDataLoaded( - LoadCallback callback, - bool success, - std::unique_ptr<EntryVector> entry_vector) { - // The database failed to load. - if (!success) { - std::move(callback).Run(false, Entries()); - return; - } - - // Success to load but no data. - if (!entry_vector) { - std::move(callback).Run(true, Entries()); - return; - } - - // Load data. - Entries entries; - for (auto it = entry_vector->begin(); it != entry_vector->end(); ++it) { - std::unique_ptr<NotificationEntry> notification_entry = - std::make_unique<NotificationEntry>(std::move(*it)); - entries.emplace_back(std::move(notification_entry)); - } - - std::move(callback).Run(true, std::move(entries)); -} - -void NotificationStore::Add(const std::string& key, - const NotificationEntry& entry, - UpdateCallback callback) { - Update(key, entry, std::move(callback)); -} - -void NotificationStore::Update(const std::string& key, - const NotificationEntry& entry, - UpdateCallback callback) { - auto entries_to_save = std::make_unique<KeyEntryVector>(); - entries_to_save->emplace_back(std::make_pair(key, entry)); - db_->UpdateEntries(std::move(entries_to_save), - std::make_unique<KeyVector>() /*keys_to_remove*/, - std::move(callback)); -} - -void NotificationStore::Delete(const std::string& key, - UpdateCallback callback) { - auto keys_to_delete = std::make_unique<KeyVector>(); - keys_to_delete->emplace_back(key); - db_->UpdateEntries(std::make_unique<KeyEntryVector>() /*entries_to_save*/, - std::move(keys_to_delete), std::move(callback)); -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/notification_store.h b/chrome/browser/notifications/scheduler/notification_store.h deleted file mode 100644 index f76e0c4..0000000 --- a/chrome/browser/notifications/scheduler/notification_store.h +++ /dev/null
@@ -1,75 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_NOTIFICATION_STORE_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_STORE_H_ - -#include <map> -#include <memory> -#include <string> -#include <utility> -#include <vector> - -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "chrome/browser/notifications/proto/notification_entry.pb.h" -#include "chrome/browser/notifications/scheduler/collection_store.h" -#include "chrome/browser/notifications/scheduler/notification_entry.h" -#include "components/leveldb_proto/public/proto_database.h" - -// Forward declaration for proto conversion. -namespace leveldb_proto { -void DataToProto(notifications::NotificationEntry* entry, - notifications::proto::NotificationEntry* proto); - -void ProtoToData(notifications::proto::NotificationEntry* proto, - notifications::NotificationEntry* entry); -} // namespace leveldb_proto - -namespace notifications { - -class NotificationStore : public CollectionStore<NotificationEntry> { - public: - NotificationStore( - std::unique_ptr<leveldb_proto::ProtoDatabase<proto::NotificationEntry, - NotificationEntry>> db); - ~NotificationStore() override; - - private: - using KeyEntryVector = std::vector<std::pair<std::string, NotificationEntry>>; - using KeyVector = std::vector<std::string>; - using EntryVector = std::vector<NotificationEntry>; - - // CollectionStore<NotificationEntry> implementation. - void InitAndLoad(LoadCallback callback) override; - void Add(const std::string& key, - const NotificationEntry& entry, - UpdateCallback callback) override; - void Update(const std::string& key, - const NotificationEntry& entry, - UpdateCallback callback) override; - void Delete(const std::string& key, UpdateCallback callback) override; - - // Called when the proto database is initialized but no yet loading the data - // into memory. - void OnDbInitialized(LoadCallback callback, - leveldb_proto::Enums::InitStatus status); - - // Called when data is loaded from |db_|. - void OnDataLoaded(LoadCallback callback, - bool success, - std::unique_ptr<EntryVector> entry_vector); - - // Level db instance to persist data. - std::unique_ptr< - leveldb_proto::ProtoDatabase<proto::NotificationEntry, NotificationEntry>> - db_; - - base::WeakPtrFactory<NotificationStore> weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(NotificationStore); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_NOTIFICATION_STORE_H_
diff --git a/chrome/browser/notifications/scheduler/notification_store_unittests.cc b/chrome/browser/notifications/scheduler/notification_store_unittests.cc deleted file mode 100644 index 69e834b5..0000000 --- a/chrome/browser/notifications/scheduler/notification_store_unittests.cc +++ /dev/null
@@ -1,176 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/notification_store.h" - -#include "base/test/scoped_task_environment.h" -#include "chrome/browser/notifications/scheduler/proto_conversion.h" -#include "chrome/browser/notifications/scheduler/test/test_utils.h" -#include "components/leveldb_proto/public/proto_database.h" -#include "components/leveldb_proto/testing/fake_db.h" -#include "testing/gtest/include/gtest/gtest.h" - -using leveldb_proto::test::FakeDB; -using InitStatus = leveldb_proto::Enums::InitStatus; -using Entries = notifications::NotificationStore::Entries; -using TestNotificationEntries = - std::map<std::string, notifications::NotificationEntry>; -using DbEntries = std::vector<notifications::NotificationEntry>; -using DbEntriesPtr = - std::unique_ptr<std::vector<notifications::NotificationEntry>>; - -namespace notifications { -namespace { - -const char kGuid[] = "1234"; - -class NotificationStoreTest : public testing::Test { - public: - NotificationStoreTest() : load_result_(false) {} - ~NotificationStoreTest() override = default; - - void SetUp() override {} - - protected: - void Init(const TestNotificationEntries& test_data, InitStatus status) { - CreateTestProtos(test_data); - auto db = - std::make_unique<FakeDB<proto::NotificationEntry, NotificationEntry>>( - &db_protos_); - db_ = db.get(); - store_ = std::make_unique<NotificationStore>(std::move(db)); - store_->InitAndLoad(base::BindOnce(&NotificationStoreTest::OnEntriesLoaded, - base::Unretained(this))); - db_->InitStatusCallback(status); - } - - bool load_result() const { return load_result_; } - const Entries& loaded_entries() const { return loaded_entries_; } - FakeDB<proto::NotificationEntry, NotificationEntry>* db() { return db_; } - CollectionStore<NotificationEntry>* store() { return store_.get(); } - - void VerifyDataInDb(DbEntriesPtr expected) { - db_->LoadEntries(base::BindOnce( - [](DbEntriesPtr expected, bool success, DbEntriesPtr entries) { - EXPECT_TRUE(success); - DCHECK(entries); - DCHECK(expected); - EXPECT_EQ(entries->size(), expected->size()); - for (size_t i = 0, size = entries->size(); i < size; ++i) { - auto& entry = (*entries)[i]; - auto& expected_entry = (*expected)[i]; - EXPECT_EQ(entry, expected_entry) - << " \n Output: " << test::DebugString(&entry) - << " \n Expected: " << test::DebugString(&expected_entry); - } - }, - std::move(expected))); - db_->LoadCallback(true); - } - - private: - // Push data into |db_|. - void CreateTestProtos(const TestNotificationEntries& test_data) { - for (const auto& pair : test_data) { - const auto& key = pair.first; - auto entry = pair.second; - proto::NotificationEntry proto; - NotificationEntryToProto(&entry, &proto); - db_protos_.emplace(key, proto); - } - } - - void OnEntriesLoaded(bool success, Entries entries) { - load_result_ = success; - loaded_entries_ = std::move(entries); - } - - base::test::ScopedTaskEnvironment scoped_task_environment_; - - // Database test objects. - FakeDB<proto::NotificationEntry, NotificationEntry>* db_; - std::map<std::string, proto::NotificationEntry> db_protos_; - - std::unique_ptr<CollectionStore<NotificationEntry>> store_; - Entries loaded_entries_; - bool load_result_; - - DISALLOW_COPY_AND_ASSIGN(NotificationStoreTest); -}; - -// Verifies initialization with empty database. -TEST_F(NotificationStoreTest, Init) { - Init(TestNotificationEntries(), InitStatus::kOK); - db()->LoadCallback(true); - EXPECT_EQ(load_result(), true); - EXPECT_TRUE(loaded_entries().empty()); -} - -// Initialize non-empty database should success. -TEST_F(NotificationStoreTest, InitSuccessWithData) { - auto test_data = TestNotificationEntries(); - NotificationEntry entry(SchedulerClientType::kTest2, kGuid); - bool success = - base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time); - DCHECK(success); - test_data.emplace(kGuid, entry); - Init(test_data, InitStatus::kOK); - db()->LoadCallback(true); - EXPECT_EQ(load_result(), true); - EXPECT_EQ(loaded_entries().size(), 1u); - EXPECT_EQ(*loaded_entries().back(), entry); -} - -// Failed database initialization will result in error. -TEST_F(NotificationStoreTest, InitFailed) { - Init(TestNotificationEntries(), InitStatus::kCorrupt); - EXPECT_EQ(load_result(), false); - EXPECT_TRUE(loaded_entries().empty()); -} - -TEST_F(NotificationStoreTest, AddAndUpdate) { - Init(TestNotificationEntries(), InitStatus::kOK); - db()->LoadCallback(true); - EXPECT_EQ(load_result(), true); - EXPECT_TRUE(loaded_entries().empty()); - - NotificationEntry entry(SchedulerClientType::kTest2, kGuid); - bool success = - base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time); - DCHECK(success); - - // Add data to the store and verify the database. - store()->Add(kGuid, entry, - base::BindOnce([](bool success) { EXPECT_TRUE(success); })); - db()->UpdateCallback(true); - auto expected = std::make_unique<DbEntries>(); - expected->emplace_back(entry); - VerifyDataInDb(std::move(expected)); - - // Update and verified the new data. - entry.notification_data.title = "test_title"; - expected = std::make_unique<DbEntries>(); - expected->emplace_back(entry); - store()->Update(kGuid, entry, - base::BindOnce([](bool success) { EXPECT_TRUE(success); })); - db()->UpdateCallback(true); - VerifyDataInDb(std::move(expected)); -} - -TEST_F(NotificationStoreTest, Delete) { - auto test_data = TestNotificationEntries(); - NotificationEntry entry(SchedulerClientType::kTest2, kGuid); - test_data.emplace(kGuid, entry); - Init(test_data, InitStatus::kOK); - db()->LoadCallback(true); - EXPECT_EQ(loaded_entries().size(), 1u); - - // Delete the entry and verify data is deleted. - store()->Delete(kGuid, base::DoNothing()); - db()->UpdateCallback(true); - VerifyDataInDb(std::make_unique<DbEntries>()); -} - -} // namespace -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/proto_conversion.cc b/chrome/browser/notifications/scheduler/proto_conversion.cc deleted file mode 100644 index 8322989..0000000 --- a/chrome/browser/notifications/scheduler/proto_conversion.cc +++ /dev/null
@@ -1,327 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/proto_conversion.h" - -#include <memory> -#include <utility> - -#include "base/logging.h" - -namespace notifications { - -namespace { - -// Helper method to convert base::TimeDelta to integer for serialization. Loses -// precision beyond miliseconds. -int64_t TimeDeltaToMilliseconds(const base::TimeDelta& delta) { - return delta.InMilliseconds(); -} - -// Helper method to convert serialized time delta as integer to base::TimeDelta -// for deserialization. Loses precision beyond miliseconds. -base::TimeDelta MillisecondsToTimeDelta(int64_t serialized_delat_ms) { - return base::TimeDelta::FromMilliseconds(serialized_delat_ms); -} - -// Helper method to convert base::Time to integer for serialization. Loses -// precision beyond miliseconds. -int64_t TimeToMilliseconds(const base::Time& time) { - return time.ToDeltaSinceWindowsEpoch().InMilliseconds(); -} - -// Helper method to convert serialized time as integer to base::Time for -// deserialization. Loses precision beyond miliseconds. -base::Time MillisecondsToTime(int64_t serialized_time_ms) { - return base::Time::FromDeltaSinceWindowsEpoch( - base::TimeDelta::FromMilliseconds(serialized_time_ms)); -} - -// Converts SchedulerClientType to its associated enum in proto buffer. -proto::SchedulerClientType ToSchedulerClientType(SchedulerClientType type) { - switch (type) { - case SchedulerClientType::kTest1: - return proto::SchedulerClientType::TEST_1; - case SchedulerClientType::kTest2: - return proto::SchedulerClientType::TEST_2; - case SchedulerClientType::kTest3: - return proto::SchedulerClientType::TEST_3; - case SchedulerClientType::kUnknown: - return proto::SchedulerClientType::UNKNOWN; - } - NOTREACHED(); -} - -// Converts SchedulerClientType from its associated enum in proto buffer. -SchedulerClientType FromSchedulerClientType( - proto::SchedulerClientType proto_type) { - switch (proto_type) { - case proto::SchedulerClientType::TEST_1: - return SchedulerClientType::kTest1; - case proto::SchedulerClientType::TEST_2: - return SchedulerClientType::kTest2; - case proto::SchedulerClientType::TEST_3: - return SchedulerClientType::kTest3; - case proto::SchedulerClientType::UNKNOWN: - return SchedulerClientType::kUnknown; - } - NOTREACHED(); -} - -// Converts UserFeedback to its associated enum in proto buffer. -proto::Impression_UserFeedback ToUserFeedback(UserFeedback feedback) { - switch (feedback) { - case UserFeedback::kNoFeedback: - return proto::Impression_UserFeedback_NO_FEEDBACK; - case UserFeedback::kHelpful: - return proto::Impression_UserFeedback_HELPFUL; - case UserFeedback::kNotHelpful: - return proto::Impression_UserFeedback_NOT_HELPFUL; - case UserFeedback::kClick: - return proto::Impression_UserFeedback_CLICK; - case UserFeedback::kDismiss: - return proto::Impression_UserFeedback_DISMISS; - case UserFeedback::kIgnore: - return proto::Impression_UserFeedback_IGNORE; - } - NOTREACHED(); -} - -// Converts UserFeedback from its associated enum in proto buffer. -UserFeedback FromUserFeedback(proto::Impression_UserFeedback feedback) { - switch (feedback) { - case proto::Impression_UserFeedback_NO_FEEDBACK: - return UserFeedback::kNoFeedback; - case proto::Impression_UserFeedback_HELPFUL: - return UserFeedback::kHelpful; - case proto::Impression_UserFeedback_NOT_HELPFUL: - return UserFeedback::kNotHelpful; - case proto::Impression_UserFeedback_CLICK: - return UserFeedback::kClick; - case proto::Impression_UserFeedback_DISMISS: - return UserFeedback::kDismiss; - case proto::Impression_UserFeedback_IGNORE: - return UserFeedback::kIgnore; - } - NOTREACHED(); -} - -// Converts ImpressionResult to its associated enum in proto buffer. -proto::Impression_ImpressionResult ToImpressionResult(ImpressionResult result) { - switch (result) { - case ImpressionResult::kInvalid: - return proto::Impression_ImpressionResult_INVALID; - case ImpressionResult::kPositive: - return proto::Impression_ImpressionResult_POSITIVE; - case ImpressionResult::kNegative: - return proto::Impression_ImpressionResult_NEGATIVE; - case ImpressionResult::kNeutral: - return proto::Impression_ImpressionResult_NEUTRAL; - } - NOTREACHED(); -} - -// Converts ImpressionResult from its associated enum in proto buffer. -ImpressionResult FromImpressionResult( - proto::Impression_ImpressionResult result) { - switch (result) { - case proto::Impression_ImpressionResult_INVALID: - return ImpressionResult::kInvalid; - case proto::Impression_ImpressionResult_POSITIVE: - return ImpressionResult::kPositive; - case proto::Impression_ImpressionResult_NEGATIVE: - return ImpressionResult::kNegative; - case proto::Impression_ImpressionResult_NEUTRAL: - return ImpressionResult::kNeutral; - } - NOTREACHED(); -} - -// Converts ImpressionResult to its associated enum in proto buffer. -proto::Impression_SchedulerTaskTime ToSchedulerTaskTime( - SchedulerTaskTime time) { - switch (time) { - case SchedulerTaskTime::kUnknown: - return proto::Impression_SchedulerTaskTime_UNKNOWN_TASK_TIME; - case SchedulerTaskTime::kMorning: - return proto::Impression_SchedulerTaskTime_MORNING; - case SchedulerTaskTime::kEvening: - return proto::Impression_SchedulerTaskTime_EVENING; - } - NOTREACHED(); -} - -// Converts ImpressionResult from its associated enum in proto buffer. -SchedulerTaskTime FromSchedulerTaskTime( - proto::Impression_SchedulerTaskTime time) { - switch (time) { - case proto::Impression_SchedulerTaskTime_UNKNOWN_TASK_TIME: - return SchedulerTaskTime::kUnknown; - case proto::Impression_SchedulerTaskTime_MORNING: - return SchedulerTaskTime::kMorning; - case proto::Impression_SchedulerTaskTime_EVENING: - return SchedulerTaskTime::kEvening; - } - NOTREACHED(); -} - -// Converts NotificationData to proto buffer type. -void NotificationDataToProto(NotificationData* notification_data, - proto::NotificationData* proto) { - proto->set_id(notification_data->id); - proto->set_title(notification_data->title); - proto->set_message(notification_data->message); - proto->set_icon_uuid(notification_data->icon_uuid); - proto->set_url(notification_data->url); -} - -// Converts NotificationData from proto buffer type. -void NotificationDataFromProto(proto::NotificationData* proto, - NotificationData* notification_data) { - notification_data->id = proto->id(); - notification_data->title = proto->title(); - notification_data->message = proto->message(); - notification_data->icon_uuid = proto->icon_uuid(); - notification_data->url = proto->url(); -} - -// Converts ScheduleParams::Priority to proto buffer type. -proto::ScheduleParams_Priority ScheduleParamsPriorityToProto( - ScheduleParams::Priority priority) { - using Priority = ScheduleParams::Priority; - switch (priority) { - case Priority::kLow: - return proto::ScheduleParams_Priority_LOW; - case Priority::kHigh: - return proto::ScheduleParams_Priority_HIGH; - case Priority::kNoThrottle: - return proto::ScheduleParams_Priority_NO_THROTTLE; - } -} - -// Converts ScheduleParams::Priority from proto buffer type. -ScheduleParams::Priority ScheduleParamsPriorityFromProto( - proto::ScheduleParams_Priority priority) { - using Priority = ScheduleParams::Priority; - switch (priority) { - case proto::ScheduleParams_Priority_LOW: - return Priority::kLow; - case proto::ScheduleParams_Priority_HIGH: - return Priority::kHigh; - case proto::ScheduleParams_Priority_NO_THROTTLE: - return Priority::kNoThrottle; - } -} - -// Converts ScheduleParams to proto buffer type. -void ScheduleParamsToProto(ScheduleParams* params, - proto::ScheduleParams* proto) { - proto->set_priority(ScheduleParamsPriorityToProto(params->priority)); -} - -// Converts ScheduleParams from proto buffer type. -void ScheduleParamsFromProto(proto::ScheduleParams* proto, - ScheduleParams* params) { - params->priority = ScheduleParamsPriorityFromProto(proto->priority()); -} - -} // namespace - -void IconEntryToProto(IconEntry* entry, notifications::proto::Icon* proto) { - proto->mutable_uuid()->swap(entry->uuid); - proto->mutable_icon()->swap(entry->data); -} - -void IconEntryFromProto(proto::Icon* proto, notifications::IconEntry* entry) { - DCHECK(proto->has_uuid()); - DCHECK(proto->has_icon()); - entry->data.swap(*proto->mutable_icon()); - entry->uuid.swap(*proto->mutable_uuid()); -} - -void ClientStateToProto(ClientState* client_state, - notifications::proto::ClientState* proto) { - proto->set_type(ToSchedulerClientType(client_state->type)); - proto->set_current_max_daily_show(client_state->current_max_daily_show); - - for (const auto& impression : client_state->impressions) { - auto* impression_ptr = proto->add_impressions(); - impression_ptr->set_create_time(TimeToMilliseconds(impression.create_time)); - impression_ptr->set_feedback(ToUserFeedback(impression.feedback)); - impression_ptr->set_impression(ToImpressionResult(impression.impression)); - impression_ptr->set_integrated(impression.integrated); - impression_ptr->set_task_start_time( - ToSchedulerTaskTime(impression.task_start_time)); - impression_ptr->set_guid(impression.guid); - } - - if (client_state->suppression_info.has_value()) { - const auto& suppression = *client_state->suppression_info; - auto* suppression_proto = proto->mutable_suppression_info(); - suppression_proto->set_last_trigger_time( - TimeToMilliseconds(suppression.last_trigger_time)); - suppression_proto->set_duration_ms( - TimeDeltaToMilliseconds(suppression.duration)); - suppression_proto->set_recover_goal(suppression.recover_goal); - } -} - -void ClientStateFromProto(proto::ClientState* proto, - notifications::ClientState* client_state) { - DCHECK(proto->has_type()); - DCHECK(proto->has_current_max_daily_show()); - client_state->type = FromSchedulerClientType(proto->type()); - client_state->current_max_daily_show = proto->current_max_daily_show(); - - for (const auto& proto_impression : proto->impressions()) { - Impression impression; - DCHECK(proto_impression.has_create_time()); - impression.create_time = MillisecondsToTime(proto_impression.create_time()); - impression.feedback = FromUserFeedback(proto_impression.feedback()); - impression.impression = FromImpressionResult(proto_impression.impression()); - impression.integrated = proto_impression.integrated(); - impression.task_start_time = - FromSchedulerTaskTime(proto_impression.task_start_time()); - impression.guid = proto_impression.guid(); - impression.type = client_state->type; - client_state->impressions.emplace_back(std::move(impression)); - } - - if (proto->has_suppression_info()) { - const auto& proto_suppression = proto->suppression_info(); - DCHECK(proto_suppression.has_last_trigger_time()); - DCHECK(proto_suppression.has_duration_ms()); - DCHECK(proto_suppression.has_recover_goal()); - - SuppressionInfo suppression_info( - MillisecondsToTime(proto_suppression.last_trigger_time()), - MillisecondsToTimeDelta(proto_suppression.duration_ms())); - suppression_info.recover_goal = proto_suppression.recover_goal(); - client_state->suppression_info = std::move(suppression_info); - } -} - -void NotificationEntryToProto(NotificationEntry* entry, - proto::NotificationEntry* proto) { - proto->set_type(ToSchedulerClientType(entry->type)); - proto->set_guid(entry->guid); - proto->set_create_time(TimeToMilliseconds(entry->create_time)); - auto* proto_notification_data = proto->mutable_notification_data(); - NotificationDataToProto(&entry->notification_data, proto_notification_data); - auto* proto_schedule_params = proto->mutable_schedule_params(); - ScheduleParamsToProto(&entry->schedule_params, proto_schedule_params); -} - -void NotificationEntryFromProto(proto::NotificationEntry* proto, - NotificationEntry* entry) { - entry->type = FromSchedulerClientType(proto->type()); - entry->guid = proto->guid(); - entry->create_time = MillisecondsToTime(proto->create_time()); - NotificationDataFromProto(proto->mutable_notification_data(), - &entry->notification_data); - ScheduleParamsFromProto(proto->mutable_schedule_params(), - &entry->schedule_params); -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/proto_conversion.h b/chrome/browser/notifications/scheduler/proto_conversion.h deleted file mode 100644 index 333f421..0000000 --- a/chrome/browser/notifications/scheduler/proto_conversion.h +++ /dev/null
@@ -1,44 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PROTO_CONVERSION_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PROTO_CONVERSION_H_ - -#include <memory> - -#include "base/macros.h" -#include "chrome/browser/notifications/proto/client_state.pb.h" -#include "chrome/browser/notifications/proto/icon.pb.h" -#include "chrome/browser/notifications/proto/notification_entry.pb.h" -#include "chrome/browser/notifications/scheduler/icon_entry.h" -#include "chrome/browser/notifications/scheduler/impression_types.h" -#include "chrome/browser/notifications/scheduler/notification_entry.h" - -namespace notifications { - -// Converts an icon entry to icon proto. -void IconEntryToProto(IconEntry* entry, notifications::proto::Icon* proto); - -// Converts an icon proto to icon entry. -void IconEntryFromProto(proto::Icon* proto, notifications::IconEntry* entry); - -// Converts client state to proto. -void ClientStateToProto(ClientState* client_state, - notifications::proto::ClientState* proto); - -// Converts proto to client state. -void ClientStateFromProto(proto::ClientState* proto, - notifications::ClientState* client_state); - -// Converts notification entry to proto. -void NotificationEntryToProto(NotificationEntry* entry, - proto::NotificationEntry* proto); - -// Converts proto to notification entry. -void NotificationEntryFromProto(proto::NotificationEntry* proto, - NotificationEntry* entry); - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PROTO_CONVERSION_H_
diff --git a/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc b/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc deleted file mode 100644 index d3b641c..0000000 --- a/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc +++ /dev/null
@@ -1,189 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/proto_conversion.h" - -#include <string> -#include <utility> -#include <vector> - -#include "base/logging.h" -#include "chrome/browser/notifications/scheduler/test/test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -using IconProto = notifications::proto::Icon; - -namespace notifications { -namespace { - -const char kUuid[] = "123"; -const char kGuid[] = "testGuid"; -const char kData[] = "bitmapdata"; - -void TestClientStateConversion(ClientState* client_state) { - DCHECK(client_state); - notifications::proto::ClientState proto; - ClientState expected; - ClientStateToProto(client_state, &proto); - ClientStateFromProto(&proto, &expected); - EXPECT_EQ(*client_state, expected) - << " \n Output: \n " << client_state->DebugPrint() << " \n Expected: \n" - << expected.DebugPrint(); -} - -void TestNotificationEntryConversion(NotificationEntry* entry) { - DCHECK(entry); - notifications::proto::NotificationEntry proto; - NotificationEntry expected(SchedulerClientType::kTest1, ""); - NotificationEntryToProto(entry, &proto); - NotificationEntryFromProto(&proto, &expected); - EXPECT_EQ(entry->notification_data, expected.notification_data); - EXPECT_EQ(entry->schedule_params, expected.schedule_params); - - EXPECT_EQ(*entry, expected) - << "Output: " << test::DebugString(entry) - << " \n Expected: " << test::DebugString(&expected); -} - -TEST(ProtoConversionTest, IconEntryFromProto) { - IconProto proto; - proto.set_uuid(kUuid); - proto.set_icon(kData); - IconEntry entry; - - IconEntryFromProto(&proto, &entry); - - // Verify entry data. - EXPECT_EQ(entry.uuid, kUuid); - EXPECT_EQ(entry.data, kData); -} - -TEST(ProtoConversionTest, IconEntryToProto) { - IconEntry entry; - entry.data = kData; - entry.uuid = kUuid; - IconProto proto; - - IconEntryToProto(&entry, &proto); - - // Verify proto data. - EXPECT_EQ(proto.icon(), kData); - EXPECT_EQ(proto.uuid(), kUuid); -} - -// Verifies client state proto conversion. -TEST(ProtoConversionTest, ClientStateProtoConversion) { - // Verify basic fields. - ClientState client_state; - test::ImpressionTestData test_data{ - SchedulerClientType::kTest1, 3, {}, base::nullopt}; - test::AddImpressionTestData(test_data, &client_state); - TestClientStateConversion(&client_state); - - // Verify suppression info. - base::Time last_trigger_time; - bool success = - base::Time::FromString("04/25/20 01:00:00 AM", &last_trigger_time); - DCHECK(success); - auto duration = base::TimeDelta::FromDays(7); - auto suppression = SuppressionInfo(last_trigger_time, duration); - suppression.recover_goal = 5; - client_state.suppression_info = std::move(suppression); - TestClientStateConversion(&client_state); -} - -// Verifies impression proto conversion. -TEST(ProtoConversionTest, ImpressionProtoConversion) { - ClientState client_state; - client_state.type = SchedulerClientType::kTest1; - base::Time create_time; - bool success = base::Time::FromString("03/25/19 00:00:00 AM", &create_time); - DCHECK(success); - Impression impression{create_time, - UserFeedback::kHelpful, - ImpressionResult::kPositive, - true /*integrated*/, - SchedulerTaskTime::kMorning, - kGuid, - SchedulerClientType::kTest1}; - client_state.impressions.emplace_back(impression); - TestClientStateConversion(&client_state); - - auto& first_impression = *client_state.impressions.begin(); - - // Verify all feedback types. - std::vector<UserFeedback> feedback_types{ - UserFeedback::kNoFeedback, UserFeedback::kHelpful, - UserFeedback::kNotHelpful, UserFeedback::kClick, - UserFeedback::kDismiss, UserFeedback::kIgnore}; - for (const auto feedback_type : feedback_types) { - first_impression.feedback = feedback_type; - TestClientStateConversion(&client_state); - } - - // Verify all impression result types. - std::vector<ImpressionResult> impression_results{ - ImpressionResult::kInvalid, ImpressionResult::kPositive, - ImpressionResult::kNegative, ImpressionResult::kNeutral}; - for (const auto impression_result : impression_results) { - first_impression.impression = impression_result; - TestClientStateConversion(&client_state); - } - - // Verify all scheduler task time types. - std::vector<SchedulerTaskTime> task_times{SchedulerTaskTime::kUnknown, - SchedulerTaskTime::kMorning, - SchedulerTaskTime::kEvening}; - for (const auto task_start_time : task_times) { - first_impression.task_start_time = task_start_time; - TestClientStateConversion(&client_state); - } -} - -// Verifies multiple impressions are serialized correctly. -TEST(ProtoConversionTest, MultipleImpressionConversion) { - ClientState client_state; - base::Time create_time; - bool success = base::Time::FromString("04/25/20 01:00:00 AM", &create_time); - DCHECK(success); - - Impression impression{create_time, UserFeedback::kHelpful, - ImpressionResult::kPositive, true, - SchedulerTaskTime::kMorning}; - Impression other_impression{create_time, UserFeedback::kNoFeedback, - ImpressionResult::kNegative, false, - SchedulerTaskTime::kEvening}; - client_state.impressions.emplace_back(std::move(impression)); - client_state.impressions.emplace_back(std::move(other_impression)); - TestClientStateConversion(&client_state); -} - -// Verifies notification entry proto conversion. -TEST(ProtoConversionTest, NotificationEntryConversion) { - NotificationEntry entry(SchedulerClientType::kTest2, kGuid); - bool success = - base::Time::FromString("04/25/20 01:00:00 AM", &entry.create_time); - DCHECK(success); - TestNotificationEntryConversion(&entry); - - // Test notification data. - entry.notification_data.id = kGuid; - entry.notification_data.title = "title"; - entry.notification_data.message = "message"; - entry.notification_data.icon_uuid = "icon_uuid"; - entry.notification_data.url = "url"; - TestNotificationEntryConversion(&entry); - - // Test scheduling params. - const ScheduleParams::Priority priorities[] = { - ScheduleParams::Priority::kLow, ScheduleParams::Priority::kHigh, - ScheduleParams::Priority::kNoThrottle}; - for (auto priority : priorities) { - entry.schedule_params.priority = priority; - TestNotificationEntryConversion(&entry); - } -} - -} // namespace -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/BUILD.gn b/chrome/browser/notifications/scheduler/public/BUILD.gn new file mode 100644 index 0000000..3ce4de5a --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/BUILD.gn
@@ -0,0 +1,41 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/buildflag_header.gni") + +if (is_android) { + import("//build/config/android/rules.gni") +} + +source_set("public") { + sources = [ + "notification_background_task_scheduler.h", + "notification_data.cc", + "notification_data.h", + "notification_params.cc", + "notification_params.h", + "notification_schedule_service.h", + "notification_scheduler_client.h", + "notification_scheduler_client_registrar.cc", + "notification_scheduler_client_registrar.h", + "notification_scheduler_types.h", + "schedule_params.cc", + "schedule_params.h", + "user_action_handler.h", + ] + + deps = [ + "//base", + "//components/keyed_service/core", + "//skia", + ] +} + +if (is_android) { + java_cpp_enum("jni_enums") { + sources = [ + "notification_scheduler_types.h", + ] + } +}
diff --git a/chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h b/chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h new file mode 100644 index 0000000..66911063 --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h
@@ -0,0 +1,60 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_BACKGROUND_TASK_SCHEDULER_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_BACKGROUND_TASK_SCHEDULER_H_ + +#include "base/macros.h" +#include "base/time/time.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" + +namespace notifications { + +// Interface to schedule a background task on platform OS to run the +// notification scheduler job. +class NotificationBackgroundTaskScheduler { + public: + // Interface used to handle background task events. + class Handler { + public: + // Called when the background task is started. + virtual void OnStartTask() = 0; + + // Called when the background task is stopped by the OS when it wants to + // reallocate resources, our task is not finished yet in this case. The + // handler implementation should explicitly decide whether the task should + // be rescheduled and run later. + virtual void OnStopTask() = 0; + + protected: + Handler() = default; + virtual ~Handler() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(Handler); + }; + + // Schedules a background task in a time window between |window_start| and + // |window_end|. This will update the current background task. Only one + // background task exists for notification scheduler. |scheduler_task_time| + // tag is passed to background task go support arbitrary time background task. + virtual void Schedule(notifications::SchedulerTaskTime scheduler_task_time, + base::TimeDelta window_start, + base::TimeDelta window_end) = 0; + + // Cancels the background task. + virtual void Cancel() = 0; + + virtual ~NotificationBackgroundTaskScheduler() = default; + + protected: + NotificationBackgroundTaskScheduler() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(NotificationBackgroundTaskScheduler); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_BACKGROUND_TASK_SCHEDULER_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_data.cc b/chrome/browser/notifications/scheduler/public/notification_data.cc new file mode 100644 index 0000000..ec6b9283 --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/notification_data.cc
@@ -0,0 +1,20 @@ +// Copyright 2019 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/notifications/scheduler/public/notification_data.h" + +namespace notifications { + +NotificationData::NotificationData() = default; + +NotificationData::NotificationData(const NotificationData& other) = default; + +bool NotificationData::operator==(const NotificationData& other) const { + return id == other.id && title == other.title && message == other.message && + icon_uuid == other.icon_uuid && url == other.url; +} + +NotificationData::~NotificationData() = default; + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/notification_data.h b/chrome/browser/notifications/scheduler/public/notification_data.h new file mode 100644 index 0000000..2bd89848 --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/notification_data.h
@@ -0,0 +1,43 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_DATA_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_DATA_H_ + +#include <string> + +namespace notifications { + +// Contains data used to display a scheduled notification. All fields will be +// persisted to disk as protobuffer NotificationData. The clients of +// notification scheduler can optionally use the texts or icon in this struct, +// or use hard coded assets id. +struct NotificationData { + NotificationData(); + NotificationData(const NotificationData& other); + bool operator==(const NotificationData& other) const; + ~NotificationData(); + + // The unique identifier of the notification. This is not used as the key for + // the database entry, but the id of other notification struct plumbed to + // the scheduler system. + std::string id; + + // The title of the notification. + std::string title; + + // The body text of the notification. + std::string message; + + // The unique identifier of the icon, which must be loaded asynchronously into + // memory. + std::string icon_uuid; + + // URL of the web site responsible for showing the notification. + std::string url; +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_DATA_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_params.cc b/chrome/browser/notifications/scheduler/public/notification_params.cc new file mode 100644 index 0000000..7b53c2a --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/notification_params.cc
@@ -0,0 +1,24 @@ +// Copyright 2019 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/notifications/scheduler/public/notification_params.h" + +#include <utility> + +#include "base/guid.h" +#include "chrome/browser/notifications/scheduler/public/schedule_params.h" + +namespace notifications { + +NotificationParams::NotificationParams(SchedulerClientType type, + NotificationData notification_data, + ScheduleParams schedule_params) + : type(type), + guid(base::GenerateGUID()), + notification_data(std::move(notification_data)), + schedule_params(std::move(schedule_params)) {} + +NotificationParams::~NotificationParams() = default; + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/notification_params.h b/chrome/browser/notifications/scheduler/public/notification_params.h new file mode 100644 index 0000000..24889dc9 --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/notification_params.h
@@ -0,0 +1,39 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_PARAMS_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_PARAMS_H_ + +#include <memory> + +#include "chrome/browser/notifications/scheduler/public/notification_data.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" +#include "chrome/browser/notifications/scheduler/public/schedule_params.h" + +namespace notifications { + +// Struct used to schedule a notification. +struct NotificationParams { + NotificationParams(SchedulerClientType type, + NotificationData notification, + ScheduleParams schedule_params); + ~NotificationParams(); + + // The type of notification using the scheduling system. + SchedulerClientType type; + + // An auto generated unique id of the scheduled notification. + std::string guid; + + // Data used to show the notification, such as text or title on the + // notification. + NotificationData notification_data; + + // Scheduling details used to determine when to show the notification. + ScheduleParams schedule_params; +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_PARAMS_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_schedule_service.h b/chrome/browser/notifications/scheduler/public/notification_schedule_service.h new file mode 100644 index 0000000..4a6ae68 --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/notification_schedule_service.h
@@ -0,0 +1,37 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULE_SERVICE_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULE_SERVICE_H_ + +#include <memory> + +#include "base/macros.h" +#include "components/keyed_service/core/keyed_service.h" + +namespace notifications { + +struct NotificationParams; + +// Service to schedule a notification to display in the future. An internal +// throttling mechanism will be applied to limit the maximum notification shown +// to the user. Also user's interaction with the notification will affect the +// frequency of notification delivery. +class NotificationScheduleService : public KeyedService { + public: + // Schedules a notification to display. + virtual void Schedule( + std::unique_ptr<NotificationParams> notification_params) = 0; + + protected: + NotificationScheduleService() = default; + ~NotificationScheduleService() override = default; + + private: + DISALLOW_COPY_AND_ASSIGN(NotificationScheduleService); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULE_SERVICE_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_scheduler_client.h b/chrome/browser/notifications/scheduler/public/notification_scheduler_client.h new file mode 100644 index 0000000..75916ce --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/notification_scheduler_client.h
@@ -0,0 +1,77 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_CLIENT_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_CLIENT_H_ + +#include <map> +#include <memory> +#include <set> +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/optional.h" +#include "chrome/browser/notifications/scheduler/public/notification_data.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace notifications { + +// The client interface to receive events from notification scheduler. +class NotificationSchedulerClient { + public: + struct DisplayData { + NotificationData notification_data; + SkBitmap icon; + }; + + // Defines user actions type. + enum class UserActionType { + // The user clicks on the notification body. + kClick = 0, + // The user clicks on the notification button. + kButtonClick = 1, + // The user dismisses the notification. + kDismiss = 2, + }; + + // Information about button clicks. + struct ButtonClickInfo { + // Unique id of the button. + std::string button_id; + + // Associate impression type for the button. + ActionButtonType type = ActionButtonType::kUnknownAction; + }; + + using DisplayCallback = + base::OnceCallback<void(std::unique_ptr<DisplayData>)>; + + NotificationSchedulerClient(); + virtual ~NotificationSchedulerClient(); + + // Called when the notification should be displayed to the user. The clients + // can overwrite data in |display_data| and return the updated data in + // |callback|. + virtual void ShowNotification(std::unique_ptr<DisplayData> display_data, + DisplayCallback callback) = 0; + + // Called when scheduler is initialized, number of notification scheduled for + // this type is reported if initialization succeeded. + virtual void OnSchedulerInitialized(bool success, + std::set<std::string> guids) = 0; + + // Called when the user interacts with the notification. + virtual void OnUserAction(UserActionType action_type, + const std::string& notification_id, + base::Optional<ButtonClickInfo> button_info) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerClient); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_CLIENT_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.cc b/chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.cc new file mode 100644 index 0000000..b5da56a03d --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.cc
@@ -0,0 +1,44 @@ +// Copyright 2019 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/notifications/scheduler/public/notification_scheduler_client_registrar.h" + +#include <utility> + +#include "base/logging.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client.h" + +namespace notifications { + +NotificationSchedulerClientRegistrar::NotificationSchedulerClientRegistrar() = + default; + +NotificationSchedulerClientRegistrar::~NotificationSchedulerClientRegistrar() = + default; + +void NotificationSchedulerClientRegistrar::RegisterClient( + SchedulerClientType type, + std::unique_ptr<NotificationSchedulerClient> client) { + DCHECK(clients_.find(type) == clients_.end()); + clients_.emplace(type, std::move(client)); +} + +NotificationSchedulerClient* NotificationSchedulerClientRegistrar::GetClient( + SchedulerClientType type) { + auto it = clients_.find(type); + if (it == clients_.end()) + return nullptr; + return it->second.get(); +} + +void NotificationSchedulerClientRegistrar::GetRegisteredClients( + std::vector<SchedulerClientType>* clients) const { + DCHECK(clients); + clients->clear(); + for (const auto& pair : clients_) { + clients->emplace_back(pair.first); + } +} + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h b/chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h new file mode 100644 index 0000000..5cca83ca --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h
@@ -0,0 +1,47 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_CLIENT_REGISTRAR_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_CLIENT_REGISTRAR_H_ + +#include <map> +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" + +namespace notifications { + +class NotificationSchedulerClient; + +// Registers and maintains a list of NotificationSchedulerClient +// implementations. +class NotificationSchedulerClientRegistrar { + public: + NotificationSchedulerClientRegistrar(); + ~NotificationSchedulerClientRegistrar(); + + // Registers a client into notification scheduler system. + void RegisterClient(SchedulerClientType type, + std::unique_ptr<NotificationSchedulerClient> client); + + // Gets a NotificationSchedulerClient, nullptr if the type doesn't exist. + NotificationSchedulerClient* GetClient(SchedulerClientType type); + + // Gets a list of registered clients, sorted by integer value of + // SchedulerClientType. + void GetRegisteredClients(std::vector<SchedulerClientType>* clients) const; + + private: + using ClientsMap = std::map<SchedulerClientType, + std::unique_ptr<NotificationSchedulerClient>>; + ClientsMap clients_; + + DISALLOW_COPY_AND_ASSIGN(NotificationSchedulerClientRegistrar); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_CLIENT_REGISTRAR_H_
diff --git a/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h b/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h new file mode 100644 index 0000000..604b4c9 --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/notification_scheduler_types.h
@@ -0,0 +1,84 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_TYPES_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_TYPES_H_ + +namespace notifications { + +// Enum to describe the time to process scheduled notification data. +// A Java counterpart will be generated for this enum. +// GENERATED_JAVA_ENUM_PACKAGE: ( +// org.chromium.chrome.browser.notifications.scheduler) +enum class SchedulerTaskTime { + // The system is started from normal user launch or other background + // tasks. + kUnknown = 0, + // Background task runs in the morning. + kMorning = 1, + // Background task runs in the evening. + kEvening = 2, +}; + +// The type of a list of clients using the notification scheduler system. +enum class SchedulerClientType { + // Test only values. + kTest1 = -1, + kTest2 = -2, + kTest3 = -3, + + // Default value of client type. + kUnknown = 0, + kMaxValue = kUnknown +}; + +// The type of user feedback from a displayed notification. +enum class UserFeedback { + // No user feedback yet. + kNoFeedback = 0, + // The user taps the helpful button, potentially a strong indicator of user's + // positive preference on the notification. + kHelpful = 1, + // The user taps the unhelpful button, potentially a strong indicator of + // user's negative preference on the notification. + kNotHelpful = 2, + // The user clicks the notification. + kClick = 3, + // The user clicks the body of the notification. + kDismiss = 4, + // The user has no interaction with the notification for a while. + kIgnore = 5, + kMaxValue = kIgnore +}; + +// The user impression of a particular notification, which may impact the +// notification display frenquency. +enum class ImpressionResult { + // Invalid user impression. + kInvalid = 0, + // Positive user impression that the user may like the notification. + kPositive = 1, + // Positive user impression that the user may dislike the notification. + kNegative = 2, + // The feedback is neutral to the user. + kNeutral = 3, + kMaxValue = kNeutral +}; + +// Categorizes type of notification buttons. Different type of button clicks +// may result in change of notification shown frequency. +enum class ActionButtonType { + // The action button is not categorized. + kUnknownAction = 0, + + // Helpful button indicates the user likes to interact with the notification. + kHelpful = 1, + + // Unhelpful button indicates dislike of the notification. + kUnhelpful = 2, +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_NOTIFICATION_SCHEDULER_TYPES_H_
diff --git a/chrome/browser/notifications/scheduler/public/schedule_params.cc b/chrome/browser/notifications/scheduler/public/schedule_params.cc new file mode 100644 index 0000000..b926dd1 --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/schedule_params.cc
@@ -0,0 +1,17 @@ +// Copyright 2019 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/notifications/scheduler/public/schedule_params.h" + +namespace notifications { + +ScheduleParams::ScheduleParams() : priority(Priority::kLow) {} + +bool ScheduleParams::operator==(const ScheduleParams& other) const { + return priority == other.priority; +} + +ScheduleParams::~ScheduleParams() = default; + +} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/public/schedule_params.h b/chrome/browser/notifications/scheduler/public/schedule_params.h new file mode 100644 index 0000000..f4ce176 --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/schedule_params.h
@@ -0,0 +1,34 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_SCHEDULE_PARAMS_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_SCHEDULE_PARAMS_H_ + +namespace notifications { + +// Specifies when to show the scheduled notification, and throttling details. +struct ScheduleParams { + enum class Priority { + // Notification may be delivered if picked by display decision layer. Most + // notification types should use this priority. + kLow, + // Notification may be delivered if picked by display decision layer. Has + // higher priority to pick as the next notification to deliver. Should not + // be used by feature frequently send notifications. + kHigh, + // No notification throttling logic is applied, every notification scheduled + // will be delivered. + kNoThrottle, + }; + + ScheduleParams(); + bool operator==(const ScheduleParams& other) const; + ~ScheduleParams(); + + Priority priority; +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_SCHEDULE_PARAMS_H_
diff --git a/chrome/browser/notifications/scheduler/public/user_action_handler.h b/chrome/browser/notifications/scheduler/public/user_action_handler.h new file mode 100644 index 0000000..8a6a19f8 --- /dev/null +++ b/chrome/browser/notifications/scheduler/public/user_action_handler.h
@@ -0,0 +1,39 @@ +// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_PUBLIC_USER_ACTION_HANDLER_H_ +#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_USER_ACTION_HANDLER_H_ + +#include <string> + +#include "base/macros.h" + +namespace notifications { + +// An interface to plumb user actions events to notification scheduling system. +// Each event needs to provide an unique id of the notification shown. +class UserActionHandler { + public: + // Called when the user clicks on the notification. + virtual void OnClick(const std::string& notification_id) = 0; + + // Called when the user clicks on a button on the notification. + virtual void OnActionClick(const std::string& notification_id, + ActionButtonType button_type) = 0; + + // Called when the user cancels or dismiss the notification. + virtual void OnDismiss(const std::string& notification_id) = 0; + + ~UserActionHandler() = default; + + protected: + UserActionHandler() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(UserActionHandler); +}; + +} // namespace notifications + +#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_PUBLIC_USER_ACTION_HANDLER_H_
diff --git a/chrome/browser/notifications/scheduler/schedule_params.cc b/chrome/browser/notifications/scheduler/schedule_params.cc deleted file mode 100644 index afedfc7b..0000000 --- a/chrome/browser/notifications/scheduler/schedule_params.cc +++ /dev/null
@@ -1,17 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/schedule_params.h" - -namespace notifications { - -ScheduleParams::ScheduleParams() : priority(Priority::kLow) {} - -bool ScheduleParams::operator==(const ScheduleParams& other) const { - return priority == other.priority; -} - -ScheduleParams::~ScheduleParams() = default; - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/schedule_params.h b/chrome/browser/notifications/scheduler/schedule_params.h deleted file mode 100644 index f999d55..0000000 --- a/chrome/browser/notifications/scheduler/schedule_params.h +++ /dev/null
@@ -1,34 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_SCHEDULE_PARAMS_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULE_PARAMS_H_ - -namespace notifications { - -// Specifies when to show the scheduled notification, and throttling details. -struct ScheduleParams { - enum class Priority { - // Notification may be delivered if picked by display decision layer. Most - // notification types should use this priority. - kLow, - // Notification may be delivered if picked by display decision layer. Has - // higher priority to pick as the next notification to deliver. Should not - // be used by feature frequently send notifications. - kHigh, - // No notification throttling logic is applied, every notification scheduled - // will be delivered. - kNoThrottle, - }; - - ScheduleParams(); - bool operator==(const ScheduleParams& other) const; - ~ScheduleParams(); - - Priority priority; -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULE_PARAMS_H_
diff --git a/chrome/browser/notifications/scheduler/schedule_service_factory_helper.cc b/chrome/browser/notifications/scheduler/schedule_service_factory_helper.cc index f9f19a9f..217c7cd 100644 --- a/chrome/browser/notifications/scheduler/schedule_service_factory_helper.cc +++ b/chrome/browser/notifications/scheduler/schedule_service_factory_helper.cc
@@ -8,18 +8,18 @@ #include "base/sequenced_task_runner.h" #include "base/task/post_task.h" -#include "chrome/browser/notifications/scheduler/display_decider.h" -#include "chrome/browser/notifications/scheduler/icon_store.h" -#include "chrome/browser/notifications/scheduler/impression_history_tracker.h" -#include "chrome/browser/notifications/scheduler/impression_store.h" -#include "chrome/browser/notifications/scheduler/init_aware_scheduler.h" -#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h" -#include "chrome/browser/notifications/scheduler/notification_schedule_service_impl.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_client_registrar.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_context.h" -#include "chrome/browser/notifications/scheduler/notification_store.h" -#include "chrome/browser/notifications/scheduler/scheduled_notification_manager.h" -#include "chrome/browser/notifications/scheduler/scheduler_config.h" +#include "chrome/browser/notifications/scheduler/internal/display_decider.h" +#include "chrome/browser/notifications/scheduler/internal/icon_store.h" +#include "chrome/browser/notifications/scheduler/internal/impression_history_tracker.h" +#include "chrome/browser/notifications/scheduler/internal/impression_store.h" +#include "chrome/browser/notifications/scheduler/internal/init_aware_scheduler.h" +#include "chrome/browser/notifications/scheduler/internal/notification_schedule_service_impl.h" +#include "chrome/browser/notifications/scheduler/internal/notification_scheduler_context.h" +#include "chrome/browser/notifications/scheduler/internal/notification_store.h" +#include "chrome/browser/notifications/scheduler/internal/scheduled_notification_manager.h" +#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h" +#include "chrome/browser/notifications/scheduler/public/notification_background_task_scheduler.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h" #include "components/leveldb_proto/public/proto_database_provider.h" #include "components/leveldb_proto/public/shared_proto_database_client_list.h"
diff --git a/chrome/browser/notifications/scheduler/scheduled_notification_manager.cc b/chrome/browser/notifications/scheduler/scheduled_notification_manager.cc deleted file mode 100644 index cd3bdf0..0000000 --- a/chrome/browser/notifications/scheduler/scheduled_notification_manager.cc +++ /dev/null
@@ -1,138 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/scheduled_notification_manager.h" - -#include <algorithm> -#include <map> - -#include "base/bind.h" -#include "base/guid.h" -#include "base/memory/weak_ptr.h" -#include "chrome/browser/notifications/scheduler/notification_entry.h" -#include "chrome/browser/notifications/scheduler/notification_params.h" - -namespace notifications { -namespace { - -// Comparator used to sort notification entries based on creation time. -bool CreateTimeCompare(const NotificationEntry* lhs, - const NotificationEntry* rhs) { - DCHECK(lhs && rhs); - return lhs->create_time <= rhs->create_time; -} - -class ScheduledNotificationManagerImpl : public ScheduledNotificationManager { - public: - using Store = std::unique_ptr<CollectionStore<NotificationEntry>>; - - ScheduledNotificationManagerImpl(Store store) - : store_(std::move(store)), delegate_(nullptr), weak_ptr_factory_(this) {} - - private: - void Init(Delegate* delegate, InitCallback callback) override { - DCHECK(!delegate_); - delegate_ = delegate; - store_->InitAndLoad( - base::BindOnce(&ScheduledNotificationManagerImpl::OnStoreInitialized, - weak_ptr_factory_.GetWeakPtr(), std::move(callback))); - } - - // NotificationManager implementation. - void ScheduleNotification( - std::unique_ptr<NotificationParams> notification_params) override { - DCHECK(notification_params); - std::string guid = notification_params->guid; - DCHECK(!guid.empty()); - if (notifications_.find(guid) != notifications_.end()) { - // TODO(xingliu): Report duplicate guid failure. - return; - } - - auto entry = - std::make_unique<NotificationEntry>(notification_params->type, guid); - entry->notification_data = - std::move(notification_params->notification_data); - entry->schedule_params = std::move(notification_params->schedule_params); - auto* entry_ptr = entry.get(); - notifications_.emplace(guid, std::move(entry)); - store_->Add( - guid, *entry_ptr, - base::BindOnce(&ScheduledNotificationManagerImpl::OnNotificationAdded, - weak_ptr_factory_.GetWeakPtr())); - } - - void DisplayNotification(const std::string& guid) override { - auto it = notifications_.find(guid); - if (it == notifications_.end()) - return; - - // Move the entry to delegate, and delete it from the storage. - auto notification_entry = std::move(it->second); - notifications_.erase(guid); - store_->Delete( - guid, - base::BindOnce(&ScheduledNotificationManagerImpl::OnNotificationDeleted, - weak_ptr_factory_.GetWeakPtr())); - if (delegate_) - delegate_->DisplayNotification(std::move(notification_entry)); - } - - void GetAllNotifications(Notifications* notifications) override { - DCHECK(notifications); - - for (const auto& pair : notifications_) { - const auto& notif = pair.second; - DCHECK(notif); - (*notifications)[notif->type].emplace_back(notif.get()); - } - - // Sort by creation time for each notification type. - for (auto it = notifications->begin(); it != notifications->end(); ++it) { - std::sort(it->second.begin(), it->second.end(), &CreateTimeCompare); - } - } - - void OnStoreInitialized(InitCallback callback, - bool success, - CollectionStore<NotificationEntry>::Entries entries) { - if (!success) { - std::move(callback).Run(false); - return; - } - - for (auto it = entries.begin(); it != entries.end(); ++it) { - std::string guid = (*it)->guid; - notifications_.emplace(guid, std::move(*it)); - } - - std::move(callback).Run(true); - } - - void OnNotificationAdded(bool success) { NOTIMPLEMENTED(); } - - void OnNotificationDeleted(bool success) { NOTIMPLEMENTED(); } - - Store store_; - Delegate* delegate_; - std::map<std::string, std::unique_ptr<NotificationEntry>> notifications_; - - base::WeakPtrFactory<ScheduledNotificationManagerImpl> weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManagerImpl); -}; - -} // namespace - -// static -std::unique_ptr<ScheduledNotificationManager> -ScheduledNotificationManager::Create( - std::unique_ptr<CollectionStore<NotificationEntry>> store) { - return std::make_unique<ScheduledNotificationManagerImpl>(std::move(store)); -} - -ScheduledNotificationManager::ScheduledNotificationManager() = default; - -ScheduledNotificationManager::~ScheduledNotificationManager() = default; - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/scheduled_notification_manager.h b/chrome/browser/notifications/scheduler/scheduled_notification_manager.h deleted file mode 100644 index 00d3716..0000000 --- a/chrome/browser/notifications/scheduler/scheduled_notification_manager.h +++ /dev/null
@@ -1,73 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_SCHEDULED_NOTIFICATION_MANAGER_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULED_NOTIFICATION_MANAGER_H_ - -#include <map> -#include <memory> -#include <string> - -#include "base/callback.h" -#include "base/macros.h" -#include "chrome/browser/notifications/scheduler/collection_store.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" - -namespace notifications { - -struct NotificationEntry; -struct NotificationParams; - -// Class to manage in-memory scheduled notifications loaded from the storage. -class ScheduledNotificationManager { - public: - using InitCallback = base::OnceCallback<void(bool)>; - using Notifications = - std::map<SchedulerClientType, std::vector<const NotificationEntry*>>; - - // Delegate that receives events from the manager. - class Delegate { - public: - // Displays a notification to the user. - virtual void DisplayNotification( - std::unique_ptr<NotificationEntry> notification) = 0; - - Delegate() = default; - virtual ~Delegate() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(Delegate); - }; - - // Creates the instance. - static std::unique_ptr<ScheduledNotificationManager> Create( - std::unique_ptr<CollectionStore<NotificationEntry>> store); - - // Initializes the notification store. - virtual void Init(Delegate* delegate, InitCallback callback) = 0; - - // Adds a new notification. - virtual void ScheduleNotification( - std::unique_ptr<NotificationParams> notification_params) = 0; - - // Displays a notification, the scheduled notification will be removed from - // storage, then Delegate::DisplayNotification() should be invoked. - virtual void DisplayNotification(const std::string& guid) = 0; - - // Gets all scheduled notifications. For each type, notifications are sorted - // by creation timestamp. - virtual void GetAllNotifications(Notifications* notifications) = 0; - - virtual ~ScheduledNotificationManager(); - - protected: - ScheduledNotificationManager(); - - private: - DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManager); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULED_NOTIFICATION_MANAGER_H_
diff --git a/chrome/browser/notifications/scheduler/scheduled_notification_manager_unittest.cc b/chrome/browser/notifications/scheduler/scheduled_notification_manager_unittest.cc deleted file mode 100644 index 0a6de34..0000000 --- a/chrome/browser/notifications/scheduler/scheduled_notification_manager_unittest.cc +++ /dev/null
@@ -1,229 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/scheduled_notification_manager.h" - -#include "base/bind.h" -#include "base/guid.h" -#include "base/run_loop.h" -#include "base/test/scoped_task_environment.h" -#include "chrome/browser/notifications/scheduler/collection_store.h" -#include "chrome/browser/notifications/scheduler/notification_entry.h" -#include "chrome/browser/notifications/scheduler/notification_params.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::_; -using ::testing::Invoke; -using Entries = std::vector<std::unique_ptr<notifications::NotificationEntry>>; - -namespace notifications { -namespace { - -const char kGuid[] = "test_guid_1234"; -const char kTitle[] = "test_title"; - -NotificationEntry CreateNotificationEntry() { - return NotificationEntry(SchedulerClientType::kUnknown, base::GenerateGUID()); -} - -class MockDelegate : public ScheduledNotificationManager::Delegate { - public: - MockDelegate() = default; - MOCK_METHOD1(DisplayNotification, void(std::unique_ptr<NotificationEntry>)); - - private: - DISALLOW_COPY_AND_ASSIGN(MockDelegate); -}; - -class MockNotificationStore : public CollectionStore<NotificationEntry> { - public: - MockNotificationStore() {} - - MOCK_METHOD1(InitAndLoad, - void(CollectionStore<NotificationEntry>::LoadCallback)); - MOCK_METHOD3(Add, - void(const std::string&, - const NotificationEntry&, - base::OnceCallback<void(bool)>)); - MOCK_METHOD3(Update, - void(const std::string&, - const NotificationEntry&, - base::OnceCallback<void(bool)>)); - MOCK_METHOD2(Delete, - void(const std::string&, base::OnceCallback<void(bool)>)); - - private: - DISALLOW_COPY_AND_ASSIGN(MockNotificationStore); -}; - -class ScheduledNotificationManagerTest : public testing::Test { - public: - ScheduledNotificationManagerTest() : store_(nullptr) {} - ~ScheduledNotificationManagerTest() override = default; - - void SetUp() override { - delegate_ = std::make_unique<MockDelegate>(); - auto store = std::make_unique<MockNotificationStore>(); - store_ = store.get(); - manager_ = ScheduledNotificationManager::Create(std::move(store)); - } - - protected: - ScheduledNotificationManager* manager() { return manager_.get(); } - MockNotificationStore* store() { return store_; } - MockDelegate* delegate() { return delegate_.get(); } - - // Initializes the manager with predefined data in the store. - void InitWithData(std::vector<NotificationEntry> data) { - Entries entries; - for (auto it = data.begin(); it != data.end(); ++it) { - auto entry_ptr = std::make_unique<NotificationEntry>( - SchedulerClientType::kUnknown, it->guid); - *(entry_ptr.get()) = *it; - entries.emplace_back(std::move(entry_ptr)); - } - - // Initialize the store and call the callback. - EXPECT_CALL(*store(), InitAndLoad(_)) - .WillOnce( - Invoke([&entries](base::OnceCallback<void(bool, Entries)> cb) { - std::move(cb).Run(true, std::move(entries)); - })); - base::RunLoop loop; - manager()->Init(delegate(), - base::BindOnce( - [](base::RepeatingClosure closure, bool success) { - EXPECT_TRUE(success); - std::move(closure).Run(); - }, - loop.QuitClosure())); - loop.Run(); - } - - private: - base::test::ScopedTaskEnvironment scoped_task_environment_; - std::unique_ptr<MockDelegate> delegate_; - MockNotificationStore* store_; - std::unique_ptr<ScheduledNotificationManager> manager_; - DISALLOW_COPY_AND_ASSIGN(ScheduledNotificationManagerTest); -}; - -// Verify that error is received when initialization failed. -TEST_F(ScheduledNotificationManagerTest, InitFailed) { - EXPECT_CALL(*store(), InitAndLoad(_)) - .WillOnce(Invoke([](base::OnceCallback<void(bool, Entries)> cb) { - std::move(cb).Run(false, Entries()); - })); - - base::RunLoop loop; - manager()->Init(delegate(), - base::BindOnce( - [](base::RepeatingClosure closure, bool success) { - // Expected to receive error. - EXPECT_FALSE(success); - std::move(closure).Run(); - }, - loop.QuitClosure())); - loop.Run(); -} - -// Test to schedule a notification. -TEST_F(ScheduledNotificationManagerTest, ScheduleNotification) { - InitWithData(std::vector<NotificationEntry>()); - NotificationData notification_data; - notification_data.title = kTitle; - ScheduleParams schedule_params; - schedule_params.priority = ScheduleParams::Priority::kHigh; - auto params = std::make_unique<NotificationParams>( - SchedulerClientType::kUnknown, notification_data, schedule_params); - std::string guid = params->guid; - EXPECT_FALSE(guid.empty()); - - // Verify call contract. - EXPECT_CALL(*store(), Add(guid, _, _)); - manager()->ScheduleNotification(std::move(params)); - - // Verify in-memory data. - ScheduledNotificationManager::Notifications notifications; - manager()->GetAllNotifications(¬ifications); - EXPECT_EQ(notifications.size(), 1u); - const NotificationEntry* entry = *(notifications.begin()->second.begin()); - EXPECT_EQ(entry->guid, guid); - EXPECT_NE(entry->create_time, base::Time()); - - // TODO(xingliu): change these to compare with operator==. - EXPECT_EQ(entry->notification_data.title, kTitle); - EXPECT_EQ(entry->schedule_params.priority, ScheduleParams::Priority::kHigh); -} - -// Test to schedule a notification without guid, we will auto generated one. -TEST_F(ScheduledNotificationManagerTest, ScheduleNotificationEmptyGuid) { - InitWithData(std::vector<NotificationEntry>()); - auto params = std::make_unique<NotificationParams>( - SchedulerClientType::kUnknown, NotificationData(), ScheduleParams()); - - // Verify call contract. - EXPECT_CALL(*store(), Add(_, _, _)); - manager()->ScheduleNotification(std::move(params)); - - // Verify in-memory data. - ScheduledNotificationManager::Notifications notifications; - manager()->GetAllNotifications(¬ifications); - EXPECT_EQ(notifications.size(), 1u); - const NotificationEntry* entry = *(notifications.begin()->second.begin()); - EXPECT_NE(entry->guid, std::string()); - EXPECT_NE(entry->create_time, base::Time()); -} - -// TODO(xingliu): change this to compare with operator==. -MATCHER_P(NotificationEntryIs, expected, "") { - return arg->guid == expected.guid; -} - -// Test to display a notification. -TEST_F(ScheduledNotificationManagerTest, DisplayNotification) { - auto entry = CreateNotificationEntry(); - entry.guid = kGuid; - InitWithData(std::vector<NotificationEntry>({entry})); - - // Verify delegate and dependency call contract. - EXPECT_CALL(*store(), Delete(kGuid, _)); - EXPECT_CALL(*delegate(), DisplayNotification(NotificationEntryIs(entry))); - manager()->DisplayNotification(kGuid); - - // Verify in-memory data. - ScheduledNotificationManager::Notifications notifications; - manager()->GetAllNotifications(¬ifications); - EXPECT_TRUE(notifications.empty()); -} - -// Verify GetAllNotifications API, the notification should be sorted based on -// creation timestamp. -TEST_F(ScheduledNotificationManagerTest, GetAllNotifications) { - // Ordering: entry1.create_time < entry0.create_time < entry2.create_time. - auto now = base::Time::Now(); - auto entry0 = CreateNotificationEntry(); - entry0.create_time = now; - auto entry1 = CreateNotificationEntry(); - entry1.create_time = now - base::TimeDelta::FromMinutes(1); - auto entry2 = CreateNotificationEntry(); - entry2.create_time = now + base::TimeDelta::FromMinutes(1); - - InitWithData(std::vector<NotificationEntry>({entry0, entry1, entry2})); - ScheduledNotificationManager::Notifications notifications; - manager()->GetAllNotifications(¬ifications); - EXPECT_EQ(notifications.size(), 1u); - EXPECT_EQ(notifications.begin()->second.size(), 3u); - - // Entries returned by GetAllNotifications() should be sorted by creation - // timestamp. - const NotificationEntry* output0 = notifications.begin()->second.front(); - const NotificationEntry* output2 = notifications.begin()->second.back(); - EXPECT_EQ(output0->create_time, entry1.create_time); - EXPECT_EQ(output2->create_time, entry2.create_time); -} - -} // namespace -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/scheduler_config.cc b/chrome/browser/notifications/scheduler/scheduler_config.cc deleted file mode 100644 index 7ded6d5..0000000 --- a/chrome/browser/notifications/scheduler/scheduler_config.cc +++ /dev/null
@@ -1,51 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/scheduler_config.h" - -namespace notifications { - -// The impression history is hold for 4 weeks. -constexpr base::TimeDelta kDefaultImpressionExpiration = - base::TimeDelta::FromDays(28); - -// The suppression lasts 8 weeks. -constexpr base::TimeDelta kDefaultSuppressionDuration = - base::TimeDelta::FromDays(56); - -// The morning task by default will run at 7am. -constexpr int kDefaultMorningTaskHour = 7; - -// The evening task by default will run at 6pm. -constexpr int kDefaultEveningTaskHour = 18; - -// Check consecutive notification dismisses in this duration to generate a -// dismiss event. -constexpr base::TimeDelta kDefaultDimissDuration = base::TimeDelta::FromDays(7); - -// Default background task time window duration. -constexpr base::TimeDelta kDefaultBackgroundTaskWindowDuration = - base::TimeDelta::FromHours(1); - -// static -std::unique_ptr<SchedulerConfig> SchedulerConfig::Create() { - return std::make_unique<SchedulerConfig>(); -} - -SchedulerConfig::SchedulerConfig() - : max_daily_shown_all_type(3), - max_daily_shown_per_type(10), - impression_expiration(kDefaultImpressionExpiration), - suppression_duration(kDefaultSuppressionDuration), - dismiss_count(3), - dismiss_duration(kDefaultDimissDuration), - morning_task_hour(kDefaultMorningTaskHour), - evening_task_hour(kDefaultEveningTaskHour), - background_task_window_duration(kDefaultBackgroundTaskWindowDuration) { - // TODO(xingliu): Add constructor using finch data. -} - -SchedulerConfig::~SchedulerConfig() = default; - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/scheduler_config.h b/chrome/browser/notifications/scheduler/scheduler_config.h deleted file mode 100644 index 415b1b0d..0000000 --- a/chrome/browser/notifications/scheduler/scheduler_config.h +++ /dev/null
@@ -1,61 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_SCHEDULER_CONFIG_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_CONFIG_H_ - -#include <memory> - -#include "base/macros.h" -#include "base/time/time.h" - -namespace notifications { - -// Configuration of notification scheduler system. -struct SchedulerConfig { - // Creates a default scheduler config. - static std::unique_ptr<SchedulerConfig> Create(); - - SchedulerConfig(); - ~SchedulerConfig(); - - // Maximum number of all types of notifications shown to the user per day. - int max_daily_shown_all_type; - - // Maximum number of notifications shown to the user per day for each type. - int max_daily_shown_per_type; - - // The time for a notification impression history data to expire. The - // impression history will be deleted then. - base::TimeDelta impression_expiration; - - // Duration of suppression when negative impression is applied. - base::TimeDelta suppression_duration; - - // The number of consecutive notification dismisses to generate a dismiss - // event. - int dismiss_count; - - // Used to check whether |dismiss_count| consecutive notification dimisses are - // in this duration, to generate a dismiss event. - base::TimeDelta dismiss_duration; - - // The hour (from 0 to 23) to run the morning background task for notification - // scheduler. - int morning_task_hour; - - // The hour (from 0 to 23) to run the evening background task for notification - // scheduler. - int evening_task_hour; - - // The time window to launch the background task. - base::TimeDelta background_task_window_duration; - - private: - DISALLOW_COPY_AND_ASSIGN(SchedulerConfig); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_CONFIG_H_
diff --git a/chrome/browser/notifications/scheduler/scheduler_utils.cc b/chrome/browser/notifications/scheduler/scheduler_utils.cc deleted file mode 100644 index f2a1d71..0000000 --- a/chrome/browser/notifications/scheduler/scheduler_utils.cc +++ /dev/null
@@ -1,61 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/scheduler_utils.h" - -#include "chrome/browser/notifications/scheduler/impression_types.h" - -namespace notifications { - -bool ToLocalHour(int hour, - const base::Time& today, - int day_delta, - base::Time* out) { - DCHECK_GE(hour, 0); - DCHECK_LE(hour, 23); - DCHECK(out); - - // Gets the local time at |hour| in yesterday. - base::Time another_day = today + base::TimeDelta::FromDays(day_delta); - base::Time::Exploded another_day_exploded; - another_day.LocalExplode(&another_day_exploded); - another_day_exploded.hour = hour; - another_day_exploded.minute = 0; - another_day_exploded.second = 0; - another_day_exploded.millisecond = 0; - - // Converts local exploded time to time stamp. - return base::Time::FromLocalExploded(another_day_exploded, out); -} - -void NotificationsShownToday( - const std::map<SchedulerClientType, const ClientState*>& client_states, - std::map<SchedulerClientType, int>* shown_per_type, - int* shown_total, - SchedulerClientType* last_shown_type) { - base::Time last_shown_time; - base::Time now(base::Time::Now()); - base::Time beginning_of_today; - bool success = ToLocalHour(0, now, 0, &beginning_of_today); - DCHECK(success); - - for (const auto& state : client_states) { - const auto* client_state = state.second; - for (const auto& impression : client_state->impressions) { - // Tracks last notification shown to the user. - if (impression.create_time > last_shown_time) { - last_shown_time = impression.create_time; - *last_shown_type = client_state->type; - } - - // Count notification shown today. - if (impression.create_time >= beginning_of_today) { - (*shown_per_type)[client_state->type]++; - ++(*shown_total); - } - } - } -} - -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/scheduler_utils.h b/chrome/browser/notifications/scheduler/scheduler_utils.h deleted file mode 100644 index cee2533d..0000000 --- a/chrome/browser/notifications/scheduler/scheduler_utils.h +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_SCHEDULER_UTILS_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_UTILS_H_ - -#include <map> - -#include "base/time/time.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" - -namespace notifications { - -struct ClientState; - -// Retrieves the time stamp of a certain hour at a certain day from today. -// |hour| must be in the range of [0, 23]. -// |today| is a timestamp to define today, usually caller can directly pass in -// the current system time. -// |day_delta| is the different between the output date and today. -// Returns false if the conversion is failed. -bool ToLocalHour(int hour, - const base::Time& today, - int day_delta, - base::Time* out); - -// Calculates the notifications shown today from impression data. -void NotificationsShownToday( - const std::map<SchedulerClientType, const ClientState*>& client_states, - std::map<SchedulerClientType, int>* shown_per_type, - int* shown_total, - SchedulerClientType* last_shown_type); - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_SCHEDULER_UTILS_H_
diff --git a/chrome/browser/notifications/scheduler/scheduler_utils_unittest.cc b/chrome/browser/notifications/scheduler/scheduler_utils_unittest.cc deleted file mode 100644 index e4acaee..0000000 --- a/chrome/browser/notifications/scheduler/scheduler_utils_unittest.cc +++ /dev/null
@@ -1,42 +0,0 @@ -// Copyright 2019 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/notifications/scheduler/scheduler_utils.h" - -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace notifications { -namespace { - -// Verifies we can get the correct time stamp at certain hour in yesterday. -TEST(SchedulerUtilsTest, ToLocalHour) { - base::Time today, another_day, expected; - - // Timestamp of another day in the past. - EXPECT_TRUE(base::Time::FromString("10/15/07 12:45:12 PM", &today)); - EXPECT_TRUE(ToLocalHour(6, today, -1, &another_day)); - EXPECT_TRUE(base::Time::FromString("10/14/07 06:00:00 AM", &expected)); - EXPECT_EQ(expected, another_day); - - EXPECT_TRUE(base::Time::FromString("03/25/19 00:00:00 AM", &today)); - EXPECT_TRUE(ToLocalHour(0, today, -1, &another_day)); - EXPECT_TRUE(base::Time::FromString("03/24/19 00:00:00 AM", &expected)); - EXPECT_EQ(expected, another_day); - - // Timestamp of the same day. - EXPECT_TRUE(base::Time::FromString("03/25/19 00:00:00 AM", &today)); - EXPECT_TRUE(ToLocalHour(0, today, 0, &another_day)); - EXPECT_TRUE(base::Time::FromString("03/25/19 00:00:00 AM", &expected)); - EXPECT_EQ(expected, another_day); - - // Timestamp of another day in the future. - EXPECT_TRUE(base::Time::FromString("03/25/19 06:35:27 AM", &today)); - EXPECT_TRUE(ToLocalHour(16, today, 7, &another_day)); - EXPECT_TRUE(base::Time::FromString("04/01/19 16:00:00 PM", &expected)); - EXPECT_EQ(expected, another_day); -} - -} // namespace -} // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/test/BUILD.gn b/chrome/browser/notifications/scheduler/test/BUILD.gn index ac37ed5..27f61b4 100644 --- a/chrome/browser/notifications/scheduler/test/BUILD.gn +++ b/chrome/browser/notifications/scheduler/test/BUILD.gn
@@ -9,7 +9,8 @@ } source_set("test_lib") { - visibility = [ "//chrome/browser/notifications/scheduler:unit_tests" ] + visibility = + [ "//chrome/browser/notifications/scheduler/internal:unit_tests" ] sources = [ "test_utils.cc", @@ -18,7 +19,7 @@ deps = [ "//base", - "//chrome/browser/notifications/scheduler:lib", - "//chrome/browser/notifications/scheduler:public", + "//chrome/browser/notifications/scheduler/internal:lib", + "//chrome/browser/notifications/scheduler/public", ] }
diff --git a/chrome/browser/notifications/scheduler/test/test_utils.cc b/chrome/browser/notifications/scheduler/test/test_utils.cc index ca00df6..760d7f1 100644 --- a/chrome/browser/notifications/scheduler/test/test_utils.cc +++ b/chrome/browser/notifications/scheduler/test/test_utils.cc
@@ -7,8 +7,8 @@ #include <sstream> #include <utility> -#include "chrome/browser/notifications/scheduler/notification_data.h" -#include "chrome/browser/notifications/scheduler/notification_entry.h" +#include "chrome/browser/notifications/scheduler/internal/notification_entry.h" +#include "chrome/browser/notifications/scheduler/public/notification_data.h" namespace notifications { namespace test {
diff --git a/chrome/browser/notifications/scheduler/test/test_utils.h b/chrome/browser/notifications/scheduler/test/test_utils.h index 9ef1a99..da79028 100644 --- a/chrome/browser/notifications/scheduler/test/test_utils.h +++ b/chrome/browser/notifications/scheduler/test/test_utils.h
@@ -9,9 +9,9 @@ #include <string> #include <vector> -#include "chrome/browser/notifications/scheduler/impression_history_tracker.h" -#include "chrome/browser/notifications/scheduler/impression_types.h" -#include "chrome/browser/notifications/scheduler/notification_scheduler_types.h" +#include "chrome/browser/notifications/scheduler/internal/impression_history_tracker.h" +#include "chrome/browser/notifications/scheduler/internal/impression_types.h" +#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h" namespace notifications {
diff --git a/chrome/browser/notifications/scheduler/user_action_handler.h b/chrome/browser/notifications/scheduler/user_action_handler.h deleted file mode 100644 index d06b7fc..0000000 --- a/chrome/browser/notifications/scheduler/user_action_handler.h +++ /dev/null
@@ -1,39 +0,0 @@ -// Copyright 2019 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_NOTIFICATIONS_SCHEDULER_USER_ACTION_HANDLER_H_ -#define CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_USER_ACTION_HANDLER_H_ - -#include <string> - -#include "base/macros.h" - -namespace notifications { - -// An interface to plumb user actions events to notification scheduling system. -// Each event needs to provide an unique id of the notification shown. -class UserActionHandler { - public: - // Called when the user clicks on the notification. - virtual void OnClick(const std::string& notification_id) = 0; - - // Called when the user clicks on a button on the notification. - virtual void OnActionClick(const std::string& notification_id, - ActionButtonType button_type) = 0; - - // Called when the user cancels or dismiss the notification. - virtual void OnDismiss(const std::string& notification_id) = 0; - - ~UserActionHandler() = default; - - protected: - UserActionHandler() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(UserActionHandler); -}; - -} // namespace notifications - -#endif // CHROME_BROWSER_NOTIFICATIONS_SCHEDULER_USER_ACTION_HANDLER_H_
diff --git a/chrome/browser/performance_manager/DEPS b/chrome/browser/performance_manager/DEPS new file mode 100644 index 0000000..86d5863 --- /dev/null +++ b/chrome/browser/performance_manager/DEPS
@@ -0,0 +1,15 @@ +# Prevent the include of Chrome specific files in general. +include_rules = [ + "-chrome/browser", + "+chrome/browser/performance_manager", +] + +# Allow some Chrome specific files to includes more things from //chrome. +specific_include_rules = { + "chrome.*": [ + "+chrome/browser", + ], + "webui_graph_dump_impl\.cc": [ + "+chrome/browser", + ], +}
diff --git a/chrome/browser/performance_manager/observers/isolation_context_metrics.cc b/chrome/browser/performance_manager/observers/isolation_context_metrics.cc index cde8d73..4476fa17 100644 --- a/chrome/browser/performance_manager/observers/isolation_context_metrics.cc +++ b/chrome/browser/performance_manager/observers/isolation_context_metrics.cc
@@ -80,11 +80,11 @@ // static const char IsolationContextMetrics::kProcessDataByTimeHistogramName[] = - "PerformanceManager.FrameSiteInstanceProcessRelationship.ByTime"; + "PerformanceManager.FrameSiteInstanceProcessRelationship.ByTime2"; // static const char IsolationContextMetrics::kProcessDataByProcessHistogramName[] = - "PerformanceManager.FrameSiteInstanceProcessRelationship.ByProcess"; + "PerformanceManager.FrameSiteInstanceProcessRelationship.ByProcess2"; // static const char @@ -160,10 +160,12 @@ // exist for renderer processes that ever actually hosted frames. const auto* process_node = ProcessNodeImpl::FromNodeBase(node); if (auto* process_data = ProcessData::Get(process_node)) { - const auto state = - process_data->has_hosted_multiple_frames_with_same_site_instance - ? ProcessDataState::kSomeFramesHaveSameSiteInstance - : ProcessDataState::kAllFramesHaveDistinctSiteInstances; + auto state = ProcessDataState::kOnlyOneFrameExists; + if (process_data->has_hosted_multiple_frames) { + state = process_data->has_hosted_multiple_frames_with_same_site_instance + ? ProcessDataState::kSomeFramesHaveSameSiteInstance + : ProcessDataState::kAllFramesHaveDistinctSiteInstances; + } UMA_HISTOGRAM_ENUMERATION(kProcessDataByProcessHistogramName, state); } } @@ -227,8 +229,11 @@ if (process_data->site_instance_frame_count.empty()) return ProcessDataState::kUndefined; - if (process_data->multi_frame_site_instance_count == 0) + if (process_data->multi_frame_site_instance_count == 0) { + if (process_data->site_instance_frame_count.size() == 1) + return ProcessDataState::kOnlyOneFrameExists; return ProcessDataState::kAllFramesHaveDistinctSiteInstances; + } return ProcessDataState::kSomeFramesHaveSameSiteInstance; } @@ -258,6 +263,13 @@ auto* data = ProcessData::GetOrCreate(process_node); const auto old_state = GetProcessDataState(data); + if (delta == 1) { + if (++data->frame_count > 1) + data->has_hosted_multiple_frames = true; + } else { + --data->frame_count; + } + auto iter = data->site_instance_frame_count .insert(std::make_pair(frame_node->site_instance_id(), 0)) .first;
diff --git a/chrome/browser/performance_manager/observers/isolation_context_metrics.h b/chrome/browser/performance_manager/observers/isolation_context_metrics.h index 60d6cf2..e64a5257 100644 --- a/chrome/browser/performance_manager/observers/isolation_context_metrics.h +++ b/chrome/browser/performance_manager/observers/isolation_context_metrics.h
@@ -75,10 +75,14 @@ // instance in the process. This is typically small for most processes, but // can go to O(100s) for power users hence the use of small_map. base::small_map<std::unordered_map<int32_t, int>> site_instance_frame_count; + // The number of frames in this process. + int frame_count = 0; // The number of site instances with multiple frames in this process. // Basically, this counts the number of entries in // |site_instance_frame_count| that are > 1. int multi_frame_site_instance_count = 0; + // Whether or not this process has *ever* hosted multiple frames. + bool has_hosted_multiple_frames = false; // Whether or not this process has *ever* hosted multiple frames in the same // site instance. This goes to true if |multi_frame_site_instance_count| is // ever greater than 0. @@ -94,8 +98,9 @@ kUndefined = -1, // This value is never reported, but used in logic. kAllFramesHaveDistinctSiteInstances = 0, kSomeFramesHaveSameSiteInstance = 1, + kOnlyOneFrameExists = 2, // Must be maintained as the max value. - kMaxValue = kSomeFramesHaveSameSiteInstance + kMaxValue = kOnlyOneFrameExists }; // Tracks summary information regarding pages in a browsing instance.
diff --git a/chrome/browser/performance_manager/observers/isolation_context_metrics_unittest.cc b/chrome/browser/performance_manager/observers/isolation_context_metrics_unittest.cc index 67398e2..8b5d3e6 100644 --- a/chrome/browser/performance_manager/observers/isolation_context_metrics_unittest.cc +++ b/chrome/browser/performance_manager/observers/isolation_context_metrics_unittest.cc
@@ -150,7 +150,7 @@ // Make up a site instance with one frame. data.site_instance_frame_count[kSID1] = 1; EXPECT_EQ(1u, data.site_instance_frame_count.size()); - EXPECT_EQ(ProcessDataState::kAllFramesHaveDistinctSiteInstances, + EXPECT_EQ(ProcessDataState::kOnlyOneFrameExists, TestIsolationContextMetrics::GetProcessDataState(&data)); // Make up another site instance with one frame. @@ -191,7 +191,7 @@ // Erase the first site instance. data.site_instance_frame_count.erase(kSID1); EXPECT_EQ(1u, data.site_instance_frame_count.size()); - EXPECT_EQ(ProcessDataState::kAllFramesHaveDistinctSiteInstances, + EXPECT_EQ(ProcessDataState::kOnlyOneFrameExists, TestIsolationContextMetrics::GetProcessDataState(&data)); } @@ -210,10 +210,12 @@ // Expect the ProcessData to exist and be correctly filled out. auto* data1 = ProcessData::GetOrCreate(process.get()); EXPECT_EQ(1u, data1->site_instance_frame_count.size()); + EXPECT_EQ(1, data1->frame_count); EXPECT_EQ(0, data1->multi_frame_site_instance_count); + EXPECT_FALSE(data1->has_hosted_multiple_frames); EXPECT_FALSE(data1->has_hosted_multiple_frames_with_same_site_instance); EXPECT_EQ(task_env().NowTicks(), data1->last_reported); - EXPECT_EQ(ProcessDataState::kAllFramesHaveDistinctSiteInstances, + EXPECT_EQ(ProcessDataState::kOnlyOneFrameExists, TestIsolationContextMetrics::GetProcessDataState(data1)); // Expect no metrics to have been emitted. @@ -228,28 +230,30 @@ EXPECT_EQ(task_env().NowTicks(), data1->last_reported); histogram_tester_.ExpectUniqueSample( metrics_->kProcessDataByTimeHistogramName, - ProcessDataState::kAllFramesHaveDistinctSiteInstances, + ProcessDataState::kOnlyOneFrameExists, metrics_->kReportingInterval.InSeconds()); histogram_tester_.ExpectTotalCount( metrics_->kProcessDataByProcessHistogramName, 0); { - // Advance time and add another frame to the same site instance, as a child + // Advance time and add another frame to a new site instance, as a child // of |frame1|. task_env().FastForwardBy(base::TimeDelta::FromSeconds(1)); auto frame2 = - CreateFrameNode(process.get(), page.get(), kBID1, kSID1, frame1.get()); - EXPECT_EQ(1u, data1->site_instance_frame_count.size()); - EXPECT_EQ(1, data1->multi_frame_site_instance_count); - EXPECT_TRUE(data1->has_hosted_multiple_frames_with_same_site_instance); - EXPECT_EQ(ProcessDataState::kSomeFramesHaveSameSiteInstance, + CreateFrameNode(process.get(), page.get(), kBID1, kSID2, frame1.get()); + EXPECT_EQ(2u, data1->site_instance_frame_count.size()); + EXPECT_EQ(2, data1->frame_count); + EXPECT_EQ(0, data1->multi_frame_site_instance_count); + EXPECT_TRUE(data1->has_hosted_multiple_frames); + EXPECT_FALSE(data1->has_hosted_multiple_frames_with_same_site_instance); + EXPECT_EQ(ProcessDataState::kAllFramesHaveDistinctSiteInstances, TestIsolationContextMetrics::GetProcessDataState(data1)); // Expect metrics to have been reported on the state change. EXPECT_EQ(task_env().NowTicks(), data1->last_reported); histogram_tester_.ExpectUniqueSample( metrics_->kProcessDataByTimeHistogramName, - ProcessDataState::kAllFramesHaveDistinctSiteInstances, + ProcessDataState::kOnlyOneFrameExists, metrics_->kReportingInterval.InSeconds() + 1); histogram_tester_.ExpectTotalCount( metrics_->kProcessDataByProcessHistogramName, 0); @@ -266,11 +270,63 @@ metrics_->kReportingInterval.InSeconds() + 2); histogram_tester_.ExpectBucketCount( metrics_->kProcessDataByTimeHistogramName, - ProcessDataState::kAllFramesHaveDistinctSiteInstances, + ProcessDataState::kAllFramesHaveDistinctSiteInstances, 1); + histogram_tester_.ExpectBucketCount( + metrics_->kProcessDataByTimeHistogramName, + ProcessDataState::kOnlyOneFrameExists, metrics_->kReportingInterval.InSeconds() + 1); + histogram_tester_.ExpectTotalCount( + metrics_->kProcessDataByProcessHistogramName, 0); + + { + // Advance time and add another frame to the same site instance, as a child + // of |frame1|. + task_env().FastForwardBy(base::TimeDelta::FromSeconds(1)); + auto frame2 = + CreateFrameNode(process.get(), page.get(), kBID1, kSID1, frame1.get()); + EXPECT_EQ(1u, data1->site_instance_frame_count.size()); + EXPECT_EQ(2, data1->frame_count); + EXPECT_EQ(1, data1->multi_frame_site_instance_count); + EXPECT_TRUE(data1->has_hosted_multiple_frames); + EXPECT_TRUE(data1->has_hosted_multiple_frames_with_same_site_instance); + EXPECT_EQ(ProcessDataState::kSomeFramesHaveSameSiteInstance, + TestIsolationContextMetrics::GetProcessDataState(data1)); + + // Expect metrics to have been reported on the state change. + EXPECT_EQ(task_env().NowTicks(), data1->last_reported); + histogram_tester_.ExpectTotalCount( + metrics_->kProcessDataByTimeHistogramName, + metrics_->kReportingInterval.InSeconds() + 3); + histogram_tester_.ExpectBucketCount( + metrics_->kProcessDataByTimeHistogramName, + ProcessDataState::kAllFramesHaveDistinctSiteInstances, 1); + histogram_tester_.ExpectBucketCount( + metrics_->kProcessDataByTimeHistogramName, + ProcessDataState::kOnlyOneFrameExists, + metrics_->kReportingInterval.InSeconds() + 2); + histogram_tester_.ExpectTotalCount( + metrics_->kProcessDataByProcessHistogramName, 0); + + // Advance time. + task_env().FastForwardBy(base::TimeDelta::FromSeconds(1)); + } + + // The second frame will be destroyed as it goes out of scope. Expect another + // flush of metrics. + EXPECT_EQ(task_env().NowTicks(), data1->last_reported); + histogram_tester_.ExpectTotalCount( + metrics_->kProcessDataByTimeHistogramName, + metrics_->kReportingInterval.InSeconds() + 4); + histogram_tester_.ExpectBucketCount( + metrics_->kProcessDataByTimeHistogramName, + ProcessDataState::kAllFramesHaveDistinctSiteInstances, 1); histogram_tester_.ExpectBucketCount( metrics_->kProcessDataByTimeHistogramName, ProcessDataState::kSomeFramesHaveSameSiteInstance, 1); + histogram_tester_.ExpectBucketCount( + metrics_->kProcessDataByTimeHistogramName, + ProcessDataState::kOnlyOneFrameExists, + metrics_->kReportingInterval.InSeconds() + 2); histogram_tester_.ExpectTotalCount( metrics_->kProcessDataByProcessHistogramName, 0);
diff --git a/chrome/browser/performance_manager/public/DEPS b/chrome/browser/performance_manager/public/DEPS new file mode 100644 index 0000000..a35d1e2 --- /dev/null +++ b/chrome/browser/performance_manager/public/DEPS
@@ -0,0 +1,5 @@ +# The public includes shouldn't depend on anything not public. +include_rules = [ + "-chrome/browser/performance_manager", + "+chrome/browser/performance_manager/public", +] \ No newline at end of file
diff --git a/chrome/browser/printing/print_dialog_cloud.cc b/chrome/browser/printing/print_dialog_cloud.cc index b3c70939..e224495 100644 --- a/chrome/browser/printing/print_dialog_cloud.cc +++ b/chrome/browser/printing/print_dialog_cloud.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/printing/print_dialog_cloud.h" +#include "base/bind.h" #include "base/macros.h" #include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/browser_process.h" @@ -27,8 +28,10 @@ class SignInObserver : public content::WebContentsObserver { public: - explicit SignInObserver(content::WebContents* web_contents) - : WebContentsObserver(web_contents), weak_ptr_factory_(this) {} + SignInObserver(content::WebContents* web_contents, base::OnceClosure callback) + : WebContentsObserver(web_contents), + callback_(std::move(callback)), + weak_ptr_factory_(this) {} private: // Overridden from content::WebContentsObserver: @@ -49,10 +52,12 @@ void WebContentsDestroyed() override { delete this; } void OnSignIn() { + std::move(callback_).Run(); if (web_contents()) web_contents()->Close(); } + base::OnceClosure callback_; base::WeakPtrFactory<SignInObserver> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(SignInObserver); @@ -60,7 +65,9 @@ } // namespace -void CreateCloudPrintSigninTab(Browser* browser, bool add_account) { +void CreateCloudPrintSigninTab(Browser* browser, + bool add_account, + base::OnceClosure callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (AccountConsistencyModeManager::IsMirrorEnabledForProfile( browser->profile())) { @@ -79,7 +86,7 @@ content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_AUTO_BOOKMARK, false)); // This observer will delete itself after destroying the WebContents. - new SignInObserver(web_contents); + new SignInObserver(web_contents, std::move(callback)); } }
diff --git a/chrome/browser/printing/print_dialog_cloud.h b/chrome/browser/printing/print_dialog_cloud.h index c94f7fc..25de632 100644 --- a/chrome/browser/printing/print_dialog_cloud.h +++ b/chrome/browser/printing/print_dialog_cloud.h
@@ -7,6 +7,8 @@ #include <string> +#include "base/callback_forward.h" + class Browser; class Profile; @@ -18,7 +20,9 @@ // Creates a tab with Google 'sign in' or 'add account' page, based on // passed |add_account| value. -void CreateCloudPrintSigninTab(Browser* browser, bool add_account); +void CreateCloudPrintSigninTab(Browser* browser, + bool add_account, + base::OnceClosure callback); // Parse switches from command_line and display the print dialog as appropriate. bool CreatePrintDialogFromCommandLine(Profile* profile,
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc index eeec06d..44b6ff8 100644 --- a/chrome/browser/push_messaging/push_messaging_service_impl.cc +++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -503,7 +503,7 @@ // GetEndpoint method ---------------------------------------------------------- -GURL PushMessagingServiceImpl::GetEndpoint(bool standard_protocol) const { +GURL PushMessagingServiceImpl::GetEndpoint(bool standard_protocol) { return GURL(kPushMessagingGcmEndpoint); }
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.h b/chrome/browser/push_messaging/push_messaging_service_impl.h index 9e75f64a..20f1b54 100644 --- a/chrome/browser/push_messaging/push_messaging_service_impl.h +++ b/chrome/browser/push_messaging/push_messaging_service_impl.h
@@ -91,7 +91,7 @@ bool CanHandle(const std::string& app_id) const override; // content::PushMessagingService implementation: - GURL GetEndpoint(bool standard_protocol) const override; + GURL GetEndpoint(bool standard_protocol) override; void SubscribeFromDocument(const GURL& requesting_origin, int64_t service_worker_registration_id, int renderer_id,
diff --git a/chrome/browser/resource_coordinator/DEPS b/chrome/browser/resource_coordinator/DEPS index aa42298..b214e26 100644 --- a/chrome/browser/resource_coordinator/DEPS +++ b/chrome/browser/resource_coordinator/DEPS
@@ -1,4 +1,7 @@ include_rules = [ + # resource_coordinator can include files from performance_manager. + "+chrome/browser/performance_manager", + "+services/resource_coordinator/public", # No inclusion of WebKit from the browser, other than strictly enum/POD, # header-only types, and some selected common code.
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/bg.js b/chrome/browser/resources/chromeos/kiosk_next_home/bg.js index 15de25dd..416a4d0 100644 --- a/chrome/browser/resources/chromeos/kiosk_next_home/bg.js +++ b/chrome/browser/resources/chromeos/kiosk_next_home/bg.js
@@ -3,6 +3,8 @@ // found in the LICENSE file. chrome.app.runtime.onLaunched.addListener(() => { - const windowOptions = {id: 'main', state: 'maximized', frame: 'none'}; - chrome.app.window.create('main.html', windowOptions); + const windowOptions = {state: 'maximized', frame: 'none'}; + chrome.app.window.create('main.html', windowOptions, (newWindow) => { + newWindow.maximize(); + }); });
diff --git a/chrome/browser/resources/chromeos/kiosk_next_home/bg_internal.js b/chrome/browser/resources/chromeos/kiosk_next_home/bg_internal.js index b2ab628..ec93f75 100644 --- a/chrome/browser/resources/chromeos/kiosk_next_home/bg_internal.js +++ b/chrome/browser/resources/chromeos/kiosk_next_home/bg_internal.js
@@ -3,6 +3,8 @@ // found in the LICENSE file. chrome.app.runtime.onLaunched.addListener(() => { - const windowOptions = {id: 'main', state: 'maximized', frame: 'none'}; - chrome.app.window.create('internal/main.html', windowOptions); + const windowOptions = {state: 'maximized', frame: 'none'}; + chrome.app.window.create('internal/main.html', windowOptions, (newWindow) => { + newWindow.maximize(); + }); });
diff --git a/chrome/browser/resources/downloads/item.js b/chrome/browser/resources/downloads/item.js index 19ffb11..5eb28c7 100644 --- a/chrome/browser/resources/downloads/item.js +++ b/chrome/browser/resources/downloads/item.js
@@ -244,6 +244,7 @@ */ computeIcon_: function() { if (loadTimeData.getBoolean('requestsApVerdicts') && + this.data && this.data.dangerType == downloads.DangerType.UNCOMMON_CONTENT) { return 'cr:error'; }
diff --git a/chrome/browser/resources/local_ntp/customize.css b/chrome/browser/resources/local_ntp/customize.css index c719258..e673900 100644 --- a/chrome/browser/resources/local_ntp/customize.css +++ b/chrome/browser/resources/local_ntp/customize.css
@@ -21,6 +21,7 @@ align-items: center; border-radius: 500px; bottom: 16px; + cursor: pointer; display: flex; height: 32px; justify-content: center; @@ -33,7 +34,6 @@ #edit-bg.ep-enhanced { background-color: rgb(255, 255, 255); box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 1px 2px rgba(0, 0, 0, 0.23); - cursor: pointer; } #edit-bg:hover,
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js index d8df1ab..2d92b167 100644 --- a/chrome/browser/resources/print_preview/data/destination_store.js +++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -924,10 +924,17 @@ } // Should be fetching the Google Drive destination. + return this.startLoadGoogleDrive(); + } + + /** + * Tries to load the Google Drive destination for the active user. + * @return {boolean} + */ + startLoadGoogleDrive() { const driveKey = print_preview.createDestinationKey( print_preview.Destination.GooglePromotedId.DOCS, print_preview.DestinationOrigin.COOKIES, this.activeUser_); - assert(key === driveKey); return this.fetchPreselectedDestination_({ id: print_preview.Destination.GooglePromotedId.DOCS, origin: print_preview.DestinationOrigin.COOKIES,
diff --git a/chrome/browser/resources/print_preview/data/user_manager.js b/chrome/browser/resources/print_preview/data/user_manager.js index 56c84c9..ac2814d 100644 --- a/chrome/browser/resources/print_preview/data/user_manager.js +++ b/chrome/browser/resources/print_preview/data/user_manager.js
@@ -62,8 +62,11 @@ this.initialized_ = false; }, - /** @param {?Array<string>} userAccounts */ - initUserAccounts: function(userAccounts) { + /** + * @param {?Array<string>} userAccounts + * @param {boolean} syncAvailable + */ + initUserAccounts: function(userAccounts, syncAvailable) { assert(!this.initialized_); this.initialized_ = true; @@ -74,9 +77,20 @@ // If cloud print is enabled, listen for account changes. assert(!this.cloudPrintDisabled); - this.addWebUIListener( - 'user-accounts-updated', this.updateUsers_.bind(this)); - this.updateUsers_(userAccounts); + if (syncAvailable) { + this.addWebUIListener( + 'user-accounts-updated', this.updateUsers_.bind(this)); + this.updateUsers_(userAccounts); + } else { + // Request the Google Docs destination from the Google Cloud Print server + // directly. We have to do this in incognito mode in order to get the + // user's login state. + this.destinationStore.startLoadGoogleDrive(); + this.addWebUIListener('check-for-account-update', () => { + this.destinationStore.startLoadCloudDestinations( + print_preview.DestinationOrigin.COOKIES); + }); + } }, /** @param {!cloudprint.CloudPrintInterface} cloudPrintInterface */
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js index da69df1..7b3f097 100644 --- a/chrome/browser/resources/print_preview/native_layer.js +++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -44,7 +44,8 @@ * serializedAppStateStr: ?string, * serializedDefaultDestinationSelectionRulesStr: ?string, * cloudPrintURL: (string | undefined), - * userAccounts: (Array<string> | undefined) + * userAccounts: (Array<string> | undefined), + * syncAvailable: boolean * }} * @see corresponding field name definitions in print_preview_handler.cc */
diff --git a/chrome/browser/resources/print_preview/polymer3/demo.js b/chrome/browser/resources/print_preview/polymer3/demo.js index 2e1415c..6a59ffd 100644 --- a/chrome/browser/resources/print_preview/polymer3/demo.js +++ b/chrome/browser/resources/print_preview/polymer3/demo.js
@@ -4,7 +4,7 @@ import 'chrome://resources/polymer/v3_0/paper-button/paper-button.js'; -import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer-element.js'; +import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; class HelloPolymer3Element extends PolymerElement { static get template() {
diff --git a/chrome/browser/resources/print_preview/ui/app.js b/chrome/browser/resources/print_preview/ui/app.js index 7c15940..3b6c9e67 100644 --- a/chrome/browser/resources/print_preview/ui/app.js +++ b/chrome/browser/resources/print_preview/ui/app.js
@@ -277,7 +277,7 @@ this.$.sidebar.init( settings.isInAppKioskMode, settings.printerName, settings.serializedDefaultDestinationSelectionRulesStr, - settings.userAccounts || null); + settings.userAccounts || null, settings.syncAvailable); this.isInKioskAutoPrintMode_ = settings.isInKioskAutoPrintMode; // This is only visible in the task manager.
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.js b/chrome/browser/resources/print_preview/ui/destination_settings.js index f8ac368..0701852 100644 --- a/chrome/browser/resources/print_preview/ui/destination_settings.js +++ b/chrome/browser/resources/print_preview/ui/destination_settings.js
@@ -177,10 +177,14 @@ * @param {string} serializedDefaultDestinationRulesStr String with rules for * selecting a default destination. * @param {?Array<string>} userAccounts The signed in user accounts. + * @param {boolean} syncAvailable Whether sync is available. Used to determine + * whether to wait for user info updates from the handler, or to always + * send requests to the Google Cloud Print server. */ init: function( - defaultPrinter, serializedDefaultDestinationRulesStr, userAccounts) { - this.$.userManager.initUserAccounts(userAccounts); + defaultPrinter, serializedDefaultDestinationRulesStr, userAccounts, + syncAvailable) { + this.$.userManager.initUserAccounts(userAccounts, syncAvailable); this.destinationStore_.init( this.appKioskMode, defaultPrinter, serializedDefaultDestinationRulesStr, /** @type {!Array<print_preview.RecentDestination>} */
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.js b/chrome/browser/resources/print_preview/ui/sidebar.js index 8ac63482..90f5915 100644 --- a/chrome/browser/resources/print_preview/ui/sidebar.js +++ b/chrome/browser/resources/print_preview/ui/sidebar.js
@@ -111,13 +111,15 @@ * @param {string} serializedDestinationSelectionRulesStr String with rules * for selecting the default destination. * @param {?Array<string>} userAccounts The signed in user accounts. + * @param {boolean} syncAvailable */ init: function( appKioskMode, defaultPrinter, serializedDestinationSelectionRulesStr, - userAccounts) { + userAccounts, syncAvailable) { this.isInAppKioskMode_ = appKioskMode; this.$.destinationSettings.init( - defaultPrinter, serializedDestinationSelectionRulesStr, userAccounts); + defaultPrinter, serializedDestinationSelectionRulesStr, userAccounts, + syncAvailable); }, /**
diff --git a/chrome/browser/resources/settings/a11y_page/switch_access_subpage.html b/chrome/browser/resources/settings/a11y_page/switch_access_subpage.html index b4caef1..409a9582 100644 --- a/chrome/browser/resources/settings/a11y_page/switch_access_subpage.html +++ b/chrome/browser/resources/settings/a11y_page/switch_access_subpage.html
@@ -22,7 +22,7 @@ $i18n{assignSelectSwitchLabel} </div> <settings-dropdown-menu label="$i18n{assignSelectSwitchLabel}" - pref="{{prefs.switch_access.select.dropdown}}" + pref="{{prefs.switch_access.select.setting}}" menu-options="[[switchAssignOptions_]]" on-settings-control-change="onSelectAssigned_"> </settings-dropdown-menu> @@ -32,7 +32,7 @@ $i18n{assignNextSwitchLabel} </div> <settings-dropdown-menu label="$i18n{assignNextSwitchLabel}" - pref="{{prefs.switch_access.next.dropdown}}" + pref="{{prefs.switch_access.next.setting}}" menu-options="[[switchAssignOptions_]]" on-settings-control-change="onNextAssigned_"> </settings-dropdown-menu> @@ -42,7 +42,7 @@ $i18n{assignPreviousSwitchLabel} </div> <settings-dropdown-menu label="$i18n{assignPreviousSwitchLabel}" - pref="{{prefs.switch_access.previous.dropdown}}" + pref="{{prefs.switch_access.previous.setting}}" menu-options="[[switchAssignOptions_]]" on-settings-control-change="onPreviousAssigned_"> </settings-dropdown-menu>
diff --git a/chrome/browser/search/iframe_source.cc b/chrome/browser/search/iframe_source.cc index 16669cc9..4eab320c 100644 --- a/chrome/browser/search/iframe_source.cc +++ b/chrome/browser/search/iframe_source.cc
@@ -18,8 +18,7 @@ IframeSource::~IframeSource() = default; -std::string IframeSource::GetMimeType( - const std::string& path_and_query) const { +std::string IframeSource::GetMimeType(const std::string& path_and_query) { std::string path(GURL("chrome-search://host/" + path_and_query).path()); if (base::EndsWith(path, ".js", base::CompareCase::INSENSITIVE_ASCII)) return "application/javascript"; @@ -34,21 +33,21 @@ return std::string(); } -bool IframeSource::AllowCaching() const { +bool IframeSource::AllowCaching() { return false; } bool IframeSource::ShouldServiceRequest( const GURL& url, content::ResourceContext* resource_context, - int render_process_id) const { + int render_process_id) { return InstantIOContext::ShouldServiceRequest(url, resource_context, render_process_id) && url.SchemeIs(chrome::kChromeSearchScheme) && url.host_piece() == GetSource() && ServesPath(url.path()); } -bool IframeSource::ShouldDenyXFrameOptions() const { +bool IframeSource::ShouldDenyXFrameOptions() { return false; }
diff --git a/chrome/browser/search/iframe_source.h b/chrome/browser/search/iframe_source.h index fba0e60..fca56596 100644 --- a/chrome/browser/search/iframe_source.h +++ b/chrome/browser/search/iframe_source.h
@@ -24,12 +24,12 @@ protected: // Overridden from content::URLDataSource: - std::string GetMimeType(const std::string& path_and_query) const override; - bool AllowCaching() const override; - bool ShouldDenyXFrameOptions() const override; + std::string GetMimeType(const std::string& path_and_query) override; + bool AllowCaching() override; + bool ShouldDenyXFrameOptions() override; bool ShouldServiceRequest(const GURL& url, content::ResourceContext* resource_context, - int render_process_id) const override; + int render_process_id) override; // Returns whether this source should serve data for a particular path. virtual bool ServesPath(const std::string& path) const = 0;
diff --git a/chrome/browser/search/iframe_source_unittest.cc b/chrome/browser/search/iframe_source_unittest.cc index 64031f3..f3514505 100644 --- a/chrome/browser/search/iframe_source_unittest.cc +++ b/chrome/browser/search/iframe_source_unittest.cc
@@ -40,7 +40,7 @@ void set_origin(std::string origin) { origin_ = origin; } protected: - std::string GetSource() const override { return "test"; } + std::string GetSource() override { return "test"; } bool ServesPath(const std::string& path) const override { return path == "/valid.html" || path == "/valid.js";
diff --git a/chrome/browser/search/local_ntp_source.cc b/chrome/browser/search/local_ntp_source.cc index 57bf875..dc577c0 100644 --- a/chrome/browser/search/local_ntp_source.cc +++ b/chrome/browser/search/local_ntp_source.cc
@@ -817,7 +817,7 @@ LocalNtpSource::~LocalNtpSource() = default; -std::string LocalNtpSource::GetSource() const { +std::string LocalNtpSource::GetSource() { return chrome::kChromeSearchLocalNtpHost; } @@ -1053,8 +1053,7 @@ callback.Run(nullptr); } -std::string LocalNtpSource::GetMimeType( - const std::string& path) const { +std::string LocalNtpSource::GetMimeType(const std::string& path) { const std::string stripped_path = StripParameters(path); for (size_t i = 0; i < base::size(kResources); ++i) { if (stripped_path == kResources[i].filename) @@ -1063,7 +1062,7 @@ return std::string(); } -bool LocalNtpSource::AllowCaching() const { +bool LocalNtpSource::AllowCaching() { // Some resources served by LocalNtpSource, i.e. config.js, are dynamically // generated and could differ on each access. To avoid using old cached // content on reload, disallow caching here. Otherwise, it fails to reflect @@ -1074,20 +1073,20 @@ bool LocalNtpSource::ShouldServiceRequest( const GURL& url, content::ResourceContext* resource_context, - int render_process_id) const { + int render_process_id) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); return ShouldServiceRequestIOThread(url, resource_context, render_process_id); } -bool LocalNtpSource::ShouldAddContentSecurityPolicy() const { +bool LocalNtpSource::ShouldAddContentSecurityPolicy() { // The Content Security Policy is served as a meta tag in local NTP html. // We disable the HTTP Header version here to avoid a conflicting policy. See // GetContentSecurityPolicy. return false; } -std::string LocalNtpSource::GetContentSecurityPolicy() const { +std::string LocalNtpSource::GetContentSecurityPolicy() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); #if !defined(GOOGLE_CHROME_BUILD) base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
diff --git a/chrome/browser/search/local_ntp_source.h b/chrome/browser/search/local_ntp_source.h index a94d670..5171b3002 100644 --- a/chrome/browser/search/local_ntp_source.h +++ b/chrome/browser/search/local_ntp_source.h
@@ -70,20 +70,20 @@ }; // Overridden from content::URLDataSource: - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string& path) const override; - bool AllowCaching() const override; + std::string GetMimeType(const std::string& path) override; + bool AllowCaching() override; bool ShouldServiceRequest(const GURL& url, content::ResourceContext* resource_context, - int render_process_id) const override; - bool ShouldAddContentSecurityPolicy() const override; + int render_process_id) override; + bool ShouldAddContentSecurityPolicy() override; // The Content Security Policy for the Local NTP. - std::string GetContentSecurityPolicy() const; + std::string GetContentSecurityPolicy(); // Overridden from NtpBackgroundServiceObserver: void OnCollectionInfoAvailable() override;
diff --git a/chrome/browser/search/most_visited_iframe_source.cc b/chrome/browser/search/most_visited_iframe_source.cc index b91a352..5aa8a9c 100644 --- a/chrome/browser/search/most_visited_iframe_source.cc +++ b/chrome/browser/search/most_visited_iframe_source.cc
@@ -50,7 +50,7 @@ MostVisitedIframeSource::~MostVisitedIframeSource() = default; -std::string MostVisitedIframeSource::GetSource() const { +std::string MostVisitedIframeSource::GetSource() { return chrome::kChromeSearchMostVisitedHost; }
diff --git a/chrome/browser/search/most_visited_iframe_source.h b/chrome/browser/search/most_visited_iframe_source.h index f4896f5..b5f3f3e 100644 --- a/chrome/browser/search/most_visited_iframe_source.h +++ b/chrome/browser/search/most_visited_iframe_source.h
@@ -28,7 +28,7 @@ private: // Overridden from IframeSource: - std::string GetSource() const override; + std::string GetSource() override; bool ServesPath(const std::string& path) const override;
diff --git a/chrome/browser/search/ntp_icon_source.cc b/chrome/browser/search/ntp_icon_source.cc index 24a4792..c6275e6 100644 --- a/chrome/browser/search/ntp_icon_source.cc +++ b/chrome/browser/search/ntp_icon_source.cc
@@ -275,7 +275,7 @@ NtpIconSource::~NtpIconSource() = default; -std::string NtpIconSource::GetSource() const { +std::string NtpIconSource::GetSource() { return chrome::kChromeUINewTabIconHost; } @@ -337,7 +337,7 @@ } } -std::string NtpIconSource::GetMimeType(const std::string&) const { +std::string NtpIconSource::GetMimeType(const std::string&) { // NOTE: this may not always be correct for all possible types that this // source will serve. Seems to work fine, however. return "image/png"; @@ -346,7 +346,7 @@ bool NtpIconSource::ShouldServiceRequest( const GURL& url, content::ResourceContext* resource_context, - int render_process_id) const { + int render_process_id) { if (url.SchemeIs(chrome::kChromeSearchScheme)) { return InstantIOContext::ShouldServiceRequest(url, resource_context, render_process_id);
diff --git a/chrome/browser/search/ntp_icon_source.h b/chrome/browser/search/ntp_icon_source.h index df56900..4c4a038 100644 --- a/chrome/browser/search/ntp_icon_source.h +++ b/chrome/browser/search/ntp_icon_source.h
@@ -35,15 +35,15 @@ ~NtpIconSource() override; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string& path) const override; + std::string GetMimeType(const std::string& path) override; bool ShouldServiceRequest(const GURL& url, content::ResourceContext* resource_context, - int render_process_id) const override; + int render_process_id) override; private: struct NtpIconRequest;
diff --git a/chrome/browser/search/suggestions/suggestions_ui.cc b/chrome/browser/search/suggestions/suggestions_ui.cc index 7677a90..39d39f1 100644 --- a/chrome/browser/search/suggestions/suggestions_ui.cc +++ b/chrome/browser/search/suggestions/suggestions_ui.cc
@@ -25,12 +25,12 @@ ~SuggestionsSourceWrapper() override; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string& path) const override; + std::string GetMimeType(const std::string& path) override; private: SuggestionsSource suggestions_source_; @@ -45,7 +45,7 @@ SuggestionsSourceWrapper::~SuggestionsSourceWrapper() {} -std::string SuggestionsSourceWrapper::GetSource() const { +std::string SuggestionsSourceWrapper::GetSource() { return chrome::kChromeUISuggestionsHost; } @@ -56,8 +56,7 @@ suggestions_source_.StartDataRequest(path, callback); } -std::string SuggestionsSourceWrapper::GetMimeType( - const std::string& path) const { +std::string SuggestionsSourceWrapper::GetMimeType(const std::string& path) { return "text/html"; }
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc b/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc index fd79e52..09744085 100644 --- a/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc +++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
@@ -401,7 +401,7 @@ bool ChromeSSLHostStateDelegate::DidHostRunInsecureContent( const std::string& host, int child_id, - InsecureContentType content_type) const { + InsecureContentType content_type) { auto entry = BrokenHostEntry(host, child_id); switch (content_type) { case MIXED_CONTENT: @@ -424,8 +424,7 @@ std::string(), nullptr); } -bool ChromeSSLHostStateDelegate::HasAllowException( - const std::string& host) const { +bool ChromeSSLHostStateDelegate::HasAllowException(const std::string& host) { GURL url = GetSecureGURLForHost(host); const ContentSettingsPattern pattern = ContentSettingsPattern::FromURLNoWildcard(url);
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate.h b/chrome/browser/ssl/chrome_ssl_host_state_delegate.h index 77b8a9f1..870949b8 100644 --- a/chrome/browser/ssl/chrome_ssl_host_state_delegate.h +++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate.h
@@ -53,12 +53,11 @@ void HostRanInsecureContent(const std::string& host, int child_id, InsecureContentType content_type) override; - bool DidHostRunInsecureContent( - const std::string& host, - int child_id, - InsecureContentType content_type) const override; + bool DidHostRunInsecureContent(const std::string& host, + int child_id, + InsecureContentType content_type) override; void RevokeUserAllowExceptions(const std::string& host) override; - bool HasAllowException(const std::string& host) const override; + bool HasAllowException(const std::string& host) override; // RevokeUserAllowExceptionsHard is the same as RevokeUserAllowExceptions but // additionally may close idle connections in the process. This should be used
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 1f55ee26a..a9ee1f3 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -3900,6 +3900,8 @@ if (is_chromeos) { sources += [ + "ash/ash_test_util.cc", + "ash/ash_test_util.h", "ash/fake_tablet_mode_controller.cc", "ash/fake_tablet_mode_controller.h", "ash/test_login_screen.cc",
diff --git a/chrome/browser/ui/ash/OWNERS b/chrome/browser/ui/ash/OWNERS index 68771a87..54238d96 100644 --- a/chrome/browser/ui/ash/OWNERS +++ b/chrome/browser/ui/ash/OWNERS
@@ -1,4 +1,3 @@ -derat@chromium.org jamescook@chromium.org oshima@chromium.org sky@chromium.org
diff --git a/chrome/browser/ui/ash/ash_test_util.cc b/chrome/browser/ui/ash/ash_test_util.cc new file mode 100644 index 0000000..2be6d2b --- /dev/null +++ b/chrome/browser/ui/ash/ash_test_util.cc
@@ -0,0 +1,78 @@ +// Copyright 2019 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/ui/ash/ash_test_util.h" + +#include "ash/public/cpp/window_properties.h" +#include "ash/public/cpp/window_state_type.h" +#include "base/run_loop.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/aura/window.h" +#include "ui/aura/window_observer.h" +#include "ui/base/test/ui_controls.h" +#include "ui/wm/core/window_util.h" + +namespace test { +namespace { +// Wait until the window's state changes to given the snapped state. +// The window should stay alive, so no need to observer destroying. +class SnapWaiter : public aura::WindowObserver { + public: + SnapWaiter(aura::Window* window, ash::WindowStateType type) + : window_(window), type_(type) { + window->AddObserver(this); + } + ~SnapWaiter() override { window_->RemoveObserver(this); } + + // aura::WindowObserver: + void OnWindowPropertyChanged(aura::Window* window, + const void* key, + intptr_t old) override { + if (key == ash::kWindowStateTypeKey && IsSnapped()) + run_loop_.Quit(); + } + + void Wait() { run_loop_.Run(); } + + bool IsSnapped() const { + return window_->GetProperty(ash::kWindowStateTypeKey) == type_; + } + + private: + aura::Window* window_; + ash::WindowStateType type_; + base::RunLoop run_loop_; + + DISALLOW_COPY_AND_ASSIGN(SnapWaiter); +}; + +} // namespace + +void ActivateAndSnapWindow(aura::Window* window, ash::WindowStateType type) { + DCHECK(window); + if (!wm::IsActiveWindow(window)) + wm::ActivateWindow(window); + + ASSERT_TRUE(wm::IsActiveWindow(window)); + + SnapWaiter snap_waiter(window, type); + ASSERT_TRUE(type == ash::WindowStateType::kRightSnapped || + type == ash::WindowStateType::kLeftSnapped); + + // Early return if it's already snapped. + if (snap_waiter.IsSnapped()) + return; + + ui_controls::SendKeyPress(window, + type == ash::WindowStateType::kLeftSnapped + ? ui::VKEY_OEM_4 + : ui::VKEY_OEM_6, + /*control=*/false, + /*shift=*/false, + /*alt=*/true, + /*command=*/false); + snap_waiter.Wait(); +} + +} // namespace test
diff --git a/chrome/browser/ui/ash/ash_test_util.h b/chrome/browser/ui/ash/ash_test_util.h new file mode 100644 index 0000000..7fd082b6 --- /dev/null +++ b/chrome/browser/ui/ash/ash_test_util.h
@@ -0,0 +1,23 @@ +// Copyright 2019 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_UI_ASH_ASH_TEST_UTIL_H_ +#define CHROME_BROWSER_UI_ASH_ASH_TEST_UTIL_H_ + +namespace aura { +class Window; +} + +namespace ash { +enum class WindowStateType; +} + +namespace test { + +// The snap window. This will activate the |window|. +void ActivateAndSnapWindow(aura::Window* window, ash::WindowStateType type); + +} // namespace test + +#endif // CHROME_BROWSER_UI_ASH_ASH_TEST_UTIL_H_
diff --git a/chrome/browser/ui/ash/split_view_interactive_uitest.cc b/chrome/browser/ui/ash/split_view_interactive_uitest.cc index f52e9c40..1f42475 100644 --- a/chrome/browser/ui/ash/split_view_interactive_uitest.cc +++ b/chrome/browser/ui/ash/split_view_interactive_uitest.cc
@@ -2,157 +2,164 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ash/public/cpp/ash_switches.h" #include "ash/public/cpp/test/shell_test_api.h" -#include "ash/shell.h" -#include "ash/wm/splitview/split_view_controller.h" +#include "ash/public/cpp/window_state_type.h" +#include "base/command_line.h" #include "base/macros.h" #include "base/run_loop.h" #include "base/system/sys_info.h" #include "base/task/post_task.h" #include "base/test/bind_test_util.h" +#include "chrome/browser/ui/ash/ash_test_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" -#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/common/webui_url_constants.h" #include "chrome/test/base/interactive_test_utils.h" +#include "chrome/test/base/perf/drag_event_generator.h" #include "chrome/test/base/perf/performance_test.h" #include "ui/aura/window.h" -#include "ui/aura/window_tree_host.h" #include "ui/compositor/compositor.h" -#include "ui/compositor/compositor_switches.h" #include "ui/display/display.h" #include "ui/display/screen.h" -#include "ui/gl/gl_switches.h" -#include "ui/views/widget/widget.h" -#include "ui/views/widget/widget_observer.h" +#include "ui/wm/core/wm_core_switches.h" namespace { -class SplitViewTest : public UIPerformanceTest { +class SplitViewTest + : public UIPerformanceTest, + public testing::WithParamInterface<::testing::tuple<bool, bool>> { public: SplitViewTest() = default; ~SplitViewTest() override = default; // UIPerformanceTest: void SetUpCommandLine(base::CommandLine* command_line) override { - command_line->AppendSwitch(ash::switches::kAshEnableTabletMode); + // We're not interested in window transition animation in this test, + // so just disable it. + command_line->AppendSwitch(wm::switches::kWindowAnimationsDisabled); + } + void SetUpOnMainThread() override { + UIPerformanceTest::SetUpOnMainThread(); + + use_ntp_ = std::get<0>(GetParam()); + use_touch_ = std::get<1>(GetParam()); + + if (use_ntp_) + ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL)); + } + + bool use_touch() const { return use_touch_; } + + void SetUMAToObserve(std::string name) { uma_name_ = std::move(name); } + + Browser* CreateBrowserMaybeWithNtp() { + Browser* new_browser = CreateBrowser(browser()->profile()); + if (use_ntp_) + ui_test_utils::NavigateToURL(new_browser, + GURL(chrome::kChromeUINewTabURL)); + return new_browser; + } + + void WaitForWarmup() { + // If running on device, wait a bit so that the display is actually powered + // on. + // TODO: this should be better factored. + base::TimeDelta warmup = base::TimeDelta::FromSeconds( + base::SysInfo::IsRunningOnChromeOS() ? 5 : 0); + // Give ntp at least 1 seconds to be fully resized. + if (use_ntp_ && warmup.is_zero()) + warmup += base::TimeDelta::FromSeconds(1); + + if (!warmup.is_zero()) { + base::RunLoop run_loop; + base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(), warmup); + run_loop.Run(); + } } private: std::vector<std::string> GetUMAHistogramNames() const override { - return {"Ash.SplitViewResize.PresentationTime.TabletMode.MultiWindow"}; + return {uma_name_}; } + bool use_ntp_; + bool use_touch_; + std::string uma_name_; + DISALLOW_COPY_AND_ASSIGN(SplitViewTest); }; -// Used to wait for a window resize to show up on screen. -class WidgetResizeWaiter : public views::WidgetObserver { - public: - explicit WidgetResizeWaiter(views::Widget* widget) : widget_(widget) { - widget_->AddObserver(this); - } +IN_PROC_BROWSER_TEST_P(SplitViewTest, ResizeTwoWindows) { + SetUMAToObserve( + "Ash.SplitViewResize.PresentationTime.TabletMode.MultiWindow"); - ~WidgetResizeWaiter() override { - widget_->RemoveObserver(this); - EXPECT_FALSE(waiting_for_frame_); - } - - void WaitForDisplay() { - do { - if (waiting_for_frame_) { - run_loop_ = std::make_unique<base::RunLoop>(); - run_loop_->Run(); - EXPECT_FALSE(waiting_for_frame_); - } - ash::ShellTestApi().WaitForNoPointerHoldLock(); - } while (waiting_for_frame_); - } - - private: - void OnFramePresented(const gfx::PresentationFeedback& feedback) { - waiting_for_frame_ = false; - if (run_loop_) - run_loop_->Quit(); - } - - // views::WidgetObserver: - void OnWidgetBoundsChanged(views::Widget* widget, - const gfx::Rect& new_bounds) override { - ui::Compositor* compositor = - widget->GetNativeWindow()->GetHost()->compositor(); - ASSERT_TRUE(compositor); - waiting_for_frame_ = true; - compositor->RequestPresentationTimeForNextFrame(base::BindOnce( - &WidgetResizeWaiter::OnFramePresented, base::Unretained(this))); - } - - views::Widget* widget_; - bool waiting_for_frame_ = false; - std::unique_ptr<base::RunLoop> run_loop_; - - DISALLOW_COPY_AND_ASSIGN(WidgetResizeWaiter); -}; - -IN_PROC_BROWSER_TEST_F(SplitViewTest, SplitViewResize) { // This test is intended to gauge performance of resizing windows while in // tablet mode split view. It does the following: // . creates two browser windows. - // . enters tablet mode. // . snaps one to the left, one to the right. + // . enters tablet mode. // . drags the resizer, which triggers resizing both browsers. - browser()->window()->Maximize(); - Browser* browser2 = CreateBrowser(browser()->profile()); - browser2->window()->Show(); - browser2->window()->Maximize(); + aura::Window* browser_window = browser()->window()->GetNativeWindow(); + test::ActivateAndSnapWindow(browser_window, + ash::WindowStateType::kLeftSnapped); - // If running on device, wait a bit so that the display is actually powered - // on. - // TODO: this should be better factored. - if (base::SysInfo::IsRunningOnChromeOS()) { - base::RunLoop run_loop; - base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(), - base::TimeDelta::FromSeconds(5)); - run_loop.Run(); - } - + Browser* browser2 = CreateBrowserMaybeWithNtp(); + aura::Window* browser2_window = browser2->window()->GetNativeWindow(); + test::ActivateAndSnapWindow(browser2_window, + ash::WindowStateType::kRightSnapped); ash::ShellTestApi().EnableTabletModeWindowManager(true); - views::Widget* browser_widget = - BrowserView::GetBrowserViewForBrowser(browser())->GetWidget(); - views::Widget* browser2_widget = - BrowserView::GetBrowserViewForBrowser(browser2)->GetWidget(); - ash::Shell* shell = ash::Shell::Get(); - shell->split_view_controller()->SnapWindow(browser2_widget->GetNativeWindow(), - ash::SplitViewController::LEFT); - shell->split_view_controller()->SnapWindow(browser_widget->GetNativeWindow(), - ash::SplitViewController::RIGHT); - - ash::ShellTestApi().WaitForNoPointerHoldLock(); + WaitForWarmup(); const gfx::Size display_size = display::Screen::GetScreen()->GetPrimaryDisplay().bounds().size(); - const gfx::Point start_position(display_size.width() / 2, - display_size.height() / 2); + const gfx::Point start_position(gfx::Rect(display_size).CenterPoint()); TRACE_EVENT_ASYNC_BEGIN0("ui", "Interaction.ui_WindowResize", this); - ASSERT_TRUE( - ui_test_utils::SendMouseMoveSync( - gfx::Point(start_position.x(), start_position.y())) && - ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, ui_controls::DOWN)); - - constexpr int kNumIncrements = 20; - int width = browser_widget->GetWindowBoundsInScreen().width(); - for (int i = 0; i < kNumIncrements; ++i) { - const gfx::Point resize_point(start_position.x() - i - 1, - start_position.y()); - WidgetResizeWaiter observer(browser_widget); - ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(resize_point)); - observer.WaitForDisplay(); - ++width; - EXPECT_EQ(width, browser_widget->GetWindowBoundsInScreen().width()); - } + gfx::Point end_position(start_position); + end_position.set_x(end_position.x() - 60); + ui_test_utils::DragEventGenerator generator( + std::make_unique<ui_test_utils::InterpolatedProducer>( + start_position, end_position, + base::TimeDelta::FromMilliseconds(1000)), + use_touch()); + generator.Wait(); TRACE_EVENT_ASYNC_END0("ui", "Interaction.ui_WindowResize", this); } +IN_PROC_BROWSER_TEST_P(SplitViewTest, ResizeWithOverview) { + SetUMAToObserve( + "Ash.SplitViewResize.PresentationTime.TabletMode.WithOverview"); + + Browser* browser2 = CreateBrowserMaybeWithNtp(); + + aura::Window* browser2_window = browser2->window()->GetNativeWindow(); + test::ActivateAndSnapWindow(browser2_window, + ash::WindowStateType::kRightSnapped); + + ash::ShellTestApi().EnableTabletModeWindowManager(true); + ash::ShellTestApi().WaitForOverviewAnimationState( + ash::OverviewAnimationState::kEnterAnimationComplete); + + WaitForWarmup(); + + const gfx::Point start_position = + display::Screen::GetScreen()->GetPrimaryDisplay().bounds().CenterPoint(); + TRACE_EVENT_ASYNC_BEGIN0("ui", "Interaction.ui_WindowResize", this); + gfx::Point end_position(start_position); + end_position.set_x(end_position.x() - 60); + ui_test_utils::DragEventGenerator generator( + std::make_unique<ui_test_utils::InterpolatedProducer>( + start_position, end_position, + base::TimeDelta::FromMilliseconds(1000)), + use_touch()); + generator.Wait(); + TRACE_EVENT_ASYNC_END0("ui", "Interaction.ui_WindowResize", this); +} + +INSTANTIATE_TEST_SUITE_P(, + SplitViewTest, + ::testing::Combine(/*ntp=*/testing::Bool(), + /*touch=*/testing::Bool())); + } // namespace
diff --git a/chrome/browser/ui/ash/window_resize_interactive_uitest.cc b/chrome/browser/ui/ash/window_resize_interactive_uitest.cc index 46cebb5..9696aff 100644 --- a/chrome/browser/ui/ash/window_resize_interactive_uitest.cc +++ b/chrome/browser/ui/ash/window_resize_interactive_uitest.cc
@@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ash/public/cpp/window_properties.h" #include "ash/public/cpp/window_state_type.h" #include "base/command_line.h" #include "base/macros.h" #include "base/run_loop.h" #include "base/task/post_task.h" +#include "chrome/browser/ui/ash/ash_test_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/test/base/interactive_test_utils.h" @@ -107,41 +107,6 @@ DISALLOW_COPY_AND_ASSIGN(MultiPointProducer); }; -// Wait until the window's state changed to given snapped state. -// The window should stay alive, so no need to observer destroying. -class SnapWaiter : public aura::WindowObserver { - public: - SnapWaiter(aura::Window* window, ash::WindowStateType type) - : window_(window), type_(type) { - window->AddObserver(this); - } - ~SnapWaiter() override { window_->RemoveObserver(this); } - - // aura::WindowObserver: - void OnWindowPropertyChanged(aura::Window* window, - const void* key, - intptr_t old) override { - if (key == ash::kWindowStateTypeKey && IsSnapped()) - run_loop_.Quit(); - } - - void Wait() { - if (!IsSnapped()) - run_loop_.Run(); - } - - private: - bool IsSnapped() const { - return window_->GetProperty(ash::kWindowStateTypeKey) == type_; - } - - aura::Window* window_; - ash::WindowStateType type_; - base::RunLoop run_loop_; - - DISALLOW_COPY_AND_ASSIGN(SnapWaiter); -}; - } // namespace // Test window resize performance in clamshell mode. @@ -191,13 +156,8 @@ IN_PROC_BROWSER_TEST_P(WindowResizeTest, Single) { aura::Window* browser_window = browser()->window()->GetNativeWindow(); - SnapWaiter snap_waiter(browser_window, ash::WindowStateType::kLeftSnapped); - ui_controls::SendKeyPress(browser_window, ui::VKEY_OEM_4, - /*control=*/false, - /*shift=*/false, - /*alt=*/true, - /*command=*/false); - snap_waiter.Wait(); + test::ActivateAndSnapWindow(browser_window, + ash::WindowStateType::kLeftSnapped); gfx::Rect bounds = browser_window->GetBoundsInScreen(); gfx::Point start_point = gfx::Point(bounds.right_center()); @@ -221,15 +181,8 @@ IN_PROC_BROWSER_TEST_P(WindowResizeTest, Multi) { aura::Window* browser_window = browser()->window()->GetNativeWindow(); - { - SnapWaiter snap_waiter(browser_window, ash::WindowStateType::kLeftSnapped); - ui_controls::SendKeyPress(browser_window, ui::VKEY_OEM_4, - /*control=*/false, - /*shift=*/false, - /*alt=*/true, - /*command=*/false); - snap_waiter.Wait(); - } + test::ActivateAndSnapWindow(browser_window, + ash::WindowStateType::kLeftSnapped); Browser* browser2 = CreateBrowser(browser()->profile()); if (use_ntp()) { @@ -237,18 +190,10 @@ ui_test_utils::NavigateToURL(browser2, ntp_url); } + aura::Window* browser_window2 = browser2->window()->GetNativeWindow(); // Snap Right - { - aura::Window* browser_window2 = browser2->window()->GetNativeWindow(); - SnapWaiter snap_waiter(browser_window2, - ash::WindowStateType::kRightSnapped); - ui_controls::SendKeyPress(browser_window2, ui::VKEY_OEM_6, - /*control=*/false, - /*shift=*/false, - /*alt=*/true, - /*command=*/false); - snap_waiter.Wait(); - } + test::ActivateAndSnapWindow(browser_window2, + ash::WindowStateType::kRightSnapped); gfx::Rect bounds = browser_window->GetBoundsInScreen(); gfx::Point start_point = gfx::Point(bounds.right_center());
diff --git a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm index 70508a8..c386235 100644 --- a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm +++ b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
@@ -64,13 +64,18 @@ // TODO(erikchen): Detect symbolic hot keys, and force control to be passed // back to AppKit so that it can handle it correctly. // https://crbug.com/846893. + auto* bridge = + remote_cocoa::NativeWidgetNSWindowBridge::GetFromNativeWindow(window); NSResponder* responder = [window firstResponder]; if ([responder conformsToProtocol:@protocol(CommandDispatcherTarget)]) { NSObject<CommandDispatcherTarget>* target = static_cast<NSObject<CommandDispatcherTarget>*>(responder); - if ([target isKeyLocked:event]) + if ([target isKeyLocked:event]) { + if (bridge) + bridge->SaveKeyEventForRedispatch(event); return ui::PerformKeyEquivalentResult::kUnhandled; + } } if ([self eventHandledByViewsFocusManager:event @@ -94,8 +99,6 @@ // highlighting of the NSMenu. CommandForKeyEventResult result = CommandForKeyEvent(event); if (result.found()) { - auto* bridge = - remote_cocoa::NativeWidgetNSWindowBridge::GetFromNativeWindow(window); if (bridge) { bool was_executed = false; bridge->host()->ExecuteCommand( @@ -103,10 +106,11 @@ true /* is_before_first_responder */, &was_executed); if (was_executed) return ui::PerformKeyEquivalentResult::kHandled; - bridge->SaveKeyEventForRedispatch(event); } } + if (bridge) + bridge->SaveKeyEventForRedispatch(event); return ui::PerformKeyEquivalentResult::kUnhandled; }
diff --git a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm index d8547f5..c3376c9 100644 --- a/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm +++ b/chrome/browser/ui/cocoa/fullscreen/fullscreen_toolbar_controller_views.mm
@@ -27,10 +27,10 @@ } - (BOOL)isFullscreenTransitionInProgress { - views::NativeWidgetMacNSWindowHost* bridge_host = + auto* host = views::NativeWidgetMacNSWindowHost::GetFromNativeWindow([self window]); - if (bridge_host->bridge_impl()) - return bridge_host->bridge_impl()->in_fullscreen_transition(); + if (auto* bridge = host->GetInProcessNSWindowBridge()) + return bridge->in_fullscreen_transition(); DLOG(ERROR) << "TODO(https://crbug.com/915110): Support fullscreen " "transitions for RemoteMacViews PWA windows."; return false; @@ -39,11 +39,11 @@ - (NSWindow*)window { NSWindow* ns_window = browserView_->GetNativeWindow().GetNativeNSWindow(); if (!ns_view_) { - views::NativeWidgetMacNSWindowHost* bridge_host = + auto* host = views::NativeWidgetMacNSWindowHost::GetFromNativeWindow(ns_window); - if (bridge_host) { - if (bridge_host->bridge_impl()) - ns_view_.reset([bridge_host->bridge_impl()->ns_view() retain]); + if (host) { + if (auto* bridge = host->GetInProcessNSWindowBridge()) + ns_view_.reset([bridge->ns_view() retain]); else DLOG(ERROR) << "Cannot retain remote NSView."; }
diff --git a/chrome/browser/ui/managed_ui.cc b/chrome/browser/ui/managed_ui.cc index 753263a..cc0fec5 100644 --- a/chrome/browser/ui/managed_ui.cc +++ b/chrome/browser/ui/managed_ui.cc
@@ -4,7 +4,6 @@ #include "chrome/browser/ui/managed_ui.h" -#include "base/feature_list.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part.h" @@ -12,7 +11,6 @@ #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/management_ui_handler.h" -#include "chrome/common/chrome_features.h" #include "chrome/grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" @@ -27,9 +25,6 @@ namespace chrome { bool ShouldDisplayManagedUi(Profile* profile) { - if (!base::FeatureList::IsEnabled(features::kShowManagedUi)) - return false; - #if defined(OS_CHROMEOS) // Don't show the UI in demo mode. if (chromeos::DemoSession::IsDeviceInDemoMode())
diff --git a/chrome/browser/ui/managed_ui_browsertest.cc b/chrome/browser/ui/managed_ui_browsertest.cc index 4f14678f..2a30ddd 100644 --- a/chrome/browser/ui/managed_ui_browsertest.cc +++ b/chrome/browser/ui/managed_ui_browsertest.cc
@@ -5,7 +5,6 @@ #include "chrome/browser/ui/managed_ui.h" #include "base/strings/utf_string_conversions.h" -#include "base/test/scoped_feature_list.h" #include "base/values.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" @@ -37,30 +36,11 @@ DISALLOW_COPY_AND_ASSIGN(ManagedUiTest); }; -IN_PROC_BROWSER_TEST_F(ManagedUiTest, ShouldDisplayManagedUiFlagDisabled) { - base::test::ScopedFeatureList feature_list; - feature_list.InitFromCommandLine("", "ShowManagedUi"); - - PolicyMap policy_map; - policy_map.Set("test-policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE, - POLICY_SOURCE_PLATFORM, - std::make_unique<base::Value>("hello world"), nullptr); - provider()->UpdateChromePolicy(policy_map); - - EXPECT_FALSE(chrome::ShouldDisplayManagedUi(browser()->profile())); -} - IN_PROC_BROWSER_TEST_F(ManagedUiTest, ShouldDisplayManagedUiNoPolicies) { - base::test::ScopedFeatureList feature_list; - feature_list.InitFromCommandLine("ShowManagedUi", ""); - EXPECT_FALSE(chrome::ShouldDisplayManagedUi(browser()->profile())); } IN_PROC_BROWSER_TEST_F(ManagedUiTest, ShouldDisplayManagedUiOnDesktop) { - base::test::ScopedFeatureList feature_list; - feature_list.InitFromCommandLine("ShowManagedUi", ""); - PolicyMap policy_map; policy_map.Set("test-policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE, POLICY_SOURCE_PLATFORM,
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc index 14fd4b8..c749627a 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc +++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
@@ -38,12 +38,12 @@ SkColor AutofillPopupBaseView::GetSelectedBackgroundColor() { return GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_FocusedHighlightedMenuItemBackgroundColor); + ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor); } SkColor AutofillPopupBaseView::GetFooterBackgroundColor() { return GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_HighlightedMenuItemBackgroundColor); + ui::NativeTheme::kColorId_BubbleFooterBackground); } SkColor AutofillPopupBaseView::GetSeparatorColor() {
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc index ce6c4233..e82ea18a 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc +++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -510,9 +510,9 @@ std::unique_ptr<views::Background> AutofillPopupSuggestionView::CreateBackground() { - return views::CreateSolidBackground( - is_selected_ ? popup_view_->GetSelectedBackgroundColor() - : popup_view_->GetBackgroundColor()); + return is_selected_ ? views::CreateSolidBackground( + popup_view_->GetSelectedBackgroundColor()) + : nullptr; } int AutofillPopupSuggestionView::GetPrimaryTextStyle() { @@ -656,9 +656,9 @@ } std::unique_ptr<views::Background> AutofillPopupFooterView::CreateBackground() { - return views::CreateSolidBackground( - is_selected_ ? popup_view_->GetSelectedBackgroundColor() - : popup_view_->GetFooterBackgroundColor()); + return is_selected_ ? views::CreateSolidBackground( + popup_view_->GetSelectedBackgroundColor()) + : nullptr; } int AutofillPopupFooterView::GetPrimaryTextStyle() { @@ -709,8 +709,6 @@ /*bottom=*/0, /*right=*/0)); AddChildView(separator); - - SetBackground(CreateBackground()); } void AutofillPopupSeparatorView::RefreshStyle() { @@ -719,7 +717,7 @@ std::unique_ptr<views::Background> AutofillPopupSeparatorView::CreateBackground() { - return views::CreateSolidBackground(popup_view_->GetBackgroundColor()); + return nullptr; } AutofillPopupSeparatorView::AutofillPopupSeparatorView( @@ -780,7 +778,7 @@ std::unique_ptr<views::Background> AutofillPopupWarningView::CreateBackground() { - return views::CreateSolidBackground(popup_view_->GetBackgroundColor()); + return nullptr; } } // namespace @@ -796,6 +794,10 @@ RefreshStyle(); } +void AutofillPopupRowView::OnThemeChanged() { + RefreshStyle(); +} + bool AutofillPopupRowView::OnMouseDragged(const ui::MouseEvent& event) { return true; } @@ -833,6 +835,20 @@ AutofillPopupViewNativeViews::~AutofillPopupViewNativeViews() {} +void AutofillPopupViewNativeViews::OnThemeChanged() { + SetBackground(views::CreateSolidBackground(GetBackgroundColor())); + // |body_container_| and |footer_container_| will be null if there is no body + // or footer content, respectively. + if (body_container_) { + body_container_->SetBackground( + views::CreateSolidBackground(GetBackgroundColor())); + } + if (footer_container_) { + footer_container_->SetBackground( + views::CreateSolidBackground(GetFooterBackgroundColor())); + } +} + void AutofillPopupViewNativeViews::Show() { DoShow(); } @@ -911,7 +927,8 @@ if (!rows_.empty()) { // Create a container to wrap the "regular" (non-footer) rows. - auto body_container = std::make_unique<views::View>(); + std::unique_ptr<views::View> body_container = + std::make_unique<views::View>(); views::BoxLayout* body_layout = body_container->SetLayoutManager( std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical)); body_layout->set_main_axis_alignment( @@ -922,11 +939,9 @@ scroll_view_ = new views::ScrollView(); scroll_view_->set_hide_horizontal_scrollbar(true); - auto* body_container_ptr = - scroll_view_->SetContents(std::move(body_container)); + body_container_ = scroll_view_->SetContents(std::move(body_container)); scroll_view_->set_draw_overflow_indicator(false); - scroll_view_->ClipHeightTo(0, - body_container_ptr->GetPreferredSize().height()); + scroll_view_->ClipHeightTo(0, body_container_->GetPreferredSize().height()); // Use an additional container to apply padding outside the scroll view, so // that the padding area is stationary. This ensures that the rounded @@ -947,9 +962,7 @@ // affected by scrolling behavior (it's "sticky") and because it has a // special background color. if (has_footer) { - views::View* footer_container = new views::View(); - footer_container->SetBackground( - views::CreateSolidBackground(GetFooterBackgroundColor())); + auto* footer_container = new views::View(); views::BoxLayout* footer_layout = footer_container->SetLayoutManager( std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical)); @@ -964,8 +977,8 @@ line_number++; } - AddChildView(footer_container); - layout_->SetFlexForView(footer_container, 0); + footer_container_ = AddChildView(footer_container); + layout_->SetFlexForView(footer_container_, 0); } }
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h index 0ce318b..54ec3645 100644 --- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h +++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
@@ -33,6 +33,7 @@ void SetSelected(bool is_selected); // views::View: + void OnThemeChanged() override; // Drags and presses on any row should be a no-op; subclasses instead rely on // entry/release events. Returns true to indicate that those events have been // processed (i.e., intentionally ignored). @@ -70,6 +71,9 @@ return rows_; } + // views::View: + void OnThemeChanged() override; + // AutofillPopupView: void Show() override; void Hide() override; @@ -97,10 +101,12 @@ void DoUpdateBoundsAndRedrawPopup() override; // Controller for this view. - AutofillPopupController* controller_; + AutofillPopupController* controller_ = nullptr; std::vector<AutofillPopupRowView*> rows_; - views::BoxLayout* layout_; - views::ScrollView* scroll_view_; + views::BoxLayout* layout_ = nullptr; + views::ScrollView* scroll_view_ = nullptr; + views::View* body_container_ = nullptr; + views::View* footer_container_ = nullptr; DISALLOW_COPY_AND_ASSIGN(AutofillPopupViewNativeViews); };
diff --git a/chrome/browser/ui/views/frame/browser_frame_mac.mm b/chrome/browser/ui/views/frame/browser_frame_mac.mm index 6542723..a9f6fb7 100644 --- a/chrome/browser/ui/views/frame/browser_frame_mac.mm +++ b/chrome/browser/ui/views/frame/browser_frame_mac.mm
@@ -332,14 +332,14 @@ } void BrowserFrameMac::OnWindowInitialized() { - if (bridge_impl()) { - bridge_impl()->SetCommandDispatcher( + if (auto* bridge = GetInProcessNSWindowBridge()) { + bridge->SetCommandDispatcher( [[[ChromeCommandDispatcherDelegate alloc] init] autorelease], [[[BrowserWindowCommandHandler alloc] init] autorelease]); } else { if (auto* host = GetHostForBrowser(browser_view_->browser())) { host->GetAppShim()->CreateCommandDispatcherForWidget( - bridge_host()->bridged_native_widget_id()); + GetNSWindowHost()->bridged_native_widget_id()); } } } @@ -409,5 +409,5 @@ // Redispatch the event. If it's a keyEquivalent:, this gives // CommandDispatcher the opportunity to finish passing the event to consumers. - return bridge_host()->RedispatchKeyEvent(event.os_event); + return GetNSWindowHost()->RedispatchKeyEvent(event.os_event); }
diff --git a/chrome/browser/ui/webui/about_ui.cc b/chrome/browser/ui/webui/about_ui.cc index f673c9c..d4cff18 100644 --- a/chrome/browser/ui/webui/about_ui.cc +++ b/chrome/browser/ui/webui/about_ui.cc
@@ -580,7 +580,7 @@ AboutUIHTMLSource::~AboutUIHTMLSource() {} -std::string AboutUIHTMLSource::GetSource() const { +std::string AboutUIHTMLSource::GetSource() { return source_name_; } @@ -640,7 +640,7 @@ callback.Run(base::RefCountedString::TakeString(&html_copy)); } -std::string AboutUIHTMLSource::GetMimeType(const std::string& path) const { +std::string AboutUIHTMLSource::GetMimeType(const std::string& path) { if (path == kCreditsJsPath || #if defined(OS_CHROMEOS) path == kKeyboardUtilsPath || @@ -652,7 +652,7 @@ return "text/html"; } -bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() const { +bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() { #if defined(OS_CHROMEOS) if (source_name_ == chrome::kChromeUIOSCreditsHost || source_name_ == chrome::kChromeUILinuxCreditsHost) { @@ -663,7 +663,7 @@ } std::string AboutUIHTMLSource::GetAccessControlAllowOriginForOrigin( - const std::string& origin) const { + const std::string& origin) { #if defined(OS_CHROMEOS) // Allow chrome://oobe to load chrome://terms via XHR. if (source_name_ == chrome::kChromeUITermsHost &&
diff --git a/chrome/browser/ui/webui/about_ui.h b/chrome/browser/ui/webui/about_ui.h index 801b98e..6860148 100644 --- a/chrome/browser/ui/webui/about_ui.h +++ b/chrome/browser/ui/webui/about_ui.h
@@ -23,15 +23,15 @@ ~AboutUIHTMLSource() override; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string& path) const override; - bool ShouldAddContentSecurityPolicy() const override; + std::string GetMimeType(const std::string& path) override; + bool ShouldAddContentSecurityPolicy() override; std::string GetAccessControlAllowOriginForOrigin( - const std::string& origin) const override; + const std::string& origin) override; // Send the response data. void FinishDataRequest(
diff --git a/chrome/browser/ui/webui/app_launcher_page_ui.cc b/chrome/browser/ui/webui/app_launcher_page_ui.cc index 3bb63dbd..7005dd6 100644 --- a/chrome/browser/ui/webui/app_launcher_page_ui.cc +++ b/chrome/browser/ui/webui/app_launcher_page_ui.cc
@@ -94,7 +94,7 @@ : profile_(profile) { } -std::string AppLauncherPageUI::HTMLSource::GetSource() const { +std::string AppLauncherPageUI::HTMLSource::GetSource() { return chrome::kChromeUIAppLauncherPageHost; } @@ -118,33 +118,30 @@ } std::string AppLauncherPageUI::HTMLSource::GetMimeType( - const std::string& resource) const { + const std::string& resource) { return "text/html"; } -bool AppLauncherPageUI::HTMLSource::ShouldReplaceExistingSource() const { +bool AppLauncherPageUI::HTMLSource::ShouldReplaceExistingSource() { return false; } -bool AppLauncherPageUI::HTMLSource::AllowCaching() const { +bool AppLauncherPageUI::HTMLSource::AllowCaching() { // Should not be cached to reflect dynamically-generated contents that may // depend on user profiles. return false; } -std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyScriptSrc() - const { +std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyScriptSrc() { // 'unsafe-inline' is added to script-src. return "script-src chrome://resources 'self' 'unsafe-eval' 'unsafe-inline';"; } -std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyStyleSrc() - const { +std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyStyleSrc() { return "style-src 'self' chrome://resources chrome://theme 'unsafe-inline';"; } -std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyImgSrc() - const { +std::string AppLauncherPageUI::HTMLSource::GetContentSecurityPolicyImgSrc() { return "img-src chrome://extension-icon chrome://theme chrome://resources " "data:;"; }
diff --git a/chrome/browser/ui/webui/app_launcher_page_ui.h b/chrome/browser/ui/webui/app_launcher_page_ui.h index 2bed0109..0b632d6a 100644 --- a/chrome/browser/ui/webui/app_launcher_page_ui.h +++ b/chrome/browser/ui/webui/app_launcher_page_ui.h
@@ -37,17 +37,17 @@ ~HTMLSource() override; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string&) const override; - bool ShouldReplaceExistingSource() const override; - bool AllowCaching() const override; - std::string GetContentSecurityPolicyScriptSrc() const override; - std::string GetContentSecurityPolicyStyleSrc() const override; - std::string GetContentSecurityPolicyImgSrc() const override; + std::string GetMimeType(const std::string&) override; + bool ShouldReplaceExistingSource() override; + bool AllowCaching() override; + std::string GetContentSecurityPolicyScriptSrc() override; + std::string GetContentSecurityPolicyStyleSrc() override; + std::string GetContentSecurityPolicyImgSrc() override; private:
diff --git a/chrome/browser/ui/webui/chromeos/cellular_setup/mobile_setup_ui.cc b/chrome/browser/ui/webui/chromeos/cellular_setup/mobile_setup_ui.cc index 060cab4..5eaf0f3 100644 --- a/chrome/browser/ui/webui/chromeos/cellular_setup/mobile_setup_ui.cc +++ b/chrome/browser/ui/webui/chromeos/cellular_setup/mobile_setup_ui.cc
@@ -175,16 +175,14 @@ ~MobileSetupUIHTMLSource() override {} // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string&) const override { - return "text/html"; - } - bool ShouldAddContentSecurityPolicy() const override { return false; } - bool AllowCaching() const override { + std::string GetMimeType(const std::string&) override { return "text/html"; } + bool ShouldAddContentSecurityPolicy() override { return false; } + bool AllowCaching() override { // Should not be cached to reflect dynamically-generated contents that may // depend on current settings. return false; @@ -266,7 +264,7 @@ MobileSetupUIHTMLSource::MobileSetupUIHTMLSource() : weak_ptr_factory_(this) {} -std::string MobileSetupUIHTMLSource::GetSource() const { +std::string MobileSetupUIHTMLSource::GetSource() { return chrome::kChromeUIMobileSetupHost; }
diff --git a/chrome/browser/ui/webui/chromeos/image_source.cc b/chrome/browser/ui/webui/chromeos/image_source.cc index 0c8e627..d5942fc 100644 --- a/chrome/browser/ui/webui/chromeos/image_source.cc +++ b/chrome/browser/ui/webui/chromeos/image_source.cc
@@ -49,7 +49,7 @@ ImageSource::~ImageSource() { } -std::string ImageSource::GetSource() const { +std::string ImageSource::GetSource() { return chrome::kChromeOSAssetHost; } @@ -87,7 +87,7 @@ } } -std::string ImageSource::GetMimeType(const std::string& path) const { +std::string ImageSource::GetMimeType(const std::string& path) { std::string mime_type; std::string ext = base::FilePath(path).Extension(); if (!ext.empty())
diff --git a/chrome/browser/ui/webui/chromeos/image_source.h b/chrome/browser/ui/webui/chromeos/image_source.h index 94f0deda1..5d7a5af 100644 --- a/chrome/browser/ui/webui/chromeos/image_source.h +++ b/chrome/browser/ui/webui/chromeos/image_source.h
@@ -26,14 +26,14 @@ ~ImageSource() override; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& got_data_callback) override; - std::string GetMimeType(const std::string& path) const override; + std::string GetMimeType(const std::string& path) override; private: // Continuation from StartDataRequest().
diff --git a/chrome/browser/ui/webui/chromeos/slow_trace_ui.cc b/chrome/browser/ui/webui/chromeos/slow_trace_ui.cc index cafa6b84..07d906c 100644 --- a/chrome/browser/ui/webui/chromeos/slow_trace_ui.cc +++ b/chrome/browser/ui/webui/chromeos/slow_trace_ui.cc
@@ -28,7 +28,7 @@ SlowTraceSource::SlowTraceSource() { } -std::string SlowTraceSource::GetSource() const { +std::string SlowTraceSource::GetSource() { return chrome::kChromeUISlowTraceHost; } @@ -51,7 +51,7 @@ callback)); } -std::string SlowTraceSource::GetMimeType(const std::string& path) const { +std::string SlowTraceSource::GetMimeType(const std::string& path) { return "application/zip"; } @@ -63,7 +63,7 @@ callback.Run(trace_data.get()); } -bool SlowTraceSource::AllowCaching() const { +bool SlowTraceSource::AllowCaching() { // Should not be cached to reflect dynamically-generated contents that may // depend on current settings. return false;
diff --git a/chrome/browser/ui/webui/chromeos/slow_trace_ui.h b/chrome/browser/ui/webui/chromeos/slow_trace_ui.h index 495b05c..77a04463 100644 --- a/chrome/browser/ui/webui/chromeos/slow_trace_ui.h +++ b/chrome/browser/ui/webui/chromeos/slow_trace_ui.h
@@ -29,13 +29,13 @@ ~SlowTraceSource() override; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string& path) const override; - bool AllowCaching() const override; + std::string GetMimeType(const std::string& path) override; + bool AllowCaching() override; private: void OnGetTraceData(const content::URLDataSource::GotDataCallback& callback,
diff --git a/chrome/browser/ui/webui/chromeos/user_image_source.cc b/chrome/browser/ui/webui/chromeos/user_image_source.cc index 1702ebc2..63c8676 100644 --- a/chrome/browser/ui/webui/chromeos/user_image_source.cc +++ b/chrome/browser/ui/webui/chromeos/user_image_source.cc
@@ -173,7 +173,7 @@ UserImageSource::~UserImageSource() {} -std::string UserImageSource::GetSource() const { +std::string UserImageSource::GetSource() { return chrome::kChromeUIUserImageHost; } @@ -189,7 +189,7 @@ callback.Run(GetUserImageInternal(account_id, frame)); } -std::string UserImageSource::GetMimeType(const std::string& path) const { +std::string UserImageSource::GetMimeType(const std::string& path) { // We need to explicitly return a mime type, otherwise if the user tries to // drag the image they get no extension. return "image/png";
diff --git a/chrome/browser/ui/webui/chromeos/user_image_source.h b/chrome/browser/ui/webui/chromeos/user_image_source.h index 287a21b..1e73aed 100644 --- a/chrome/browser/ui/webui/chromeos/user_image_source.h +++ b/chrome/browser/ui/webui/chromeos/user_image_source.h
@@ -30,12 +30,12 @@ ~UserImageSource() override; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string& path) const override; + std::string GetMimeType(const std::string& path) override; // Returns PNG encoded image for user with specified |account_id|. If there's // no user with such an id, returns the first default image. Always returns
diff --git a/chrome/browser/ui/webui/chromeos/video_source.cc b/chrome/browser/ui/webui/chromeos/video_source.cc index c42955ad..4db1b33 100644 --- a/chrome/browser/ui/webui/chromeos/video_source.cc +++ b/chrome/browser/ui/webui/chromeos/video_source.cc
@@ -62,7 +62,7 @@ VideoSource::~VideoSource() {} -std::string VideoSource::GetSource() const { +std::string VideoSource::GetSource() { return chrome::kChromeOSAssetHost; } @@ -85,7 +85,7 @@ got_data_callback)); } -std::string VideoSource::GetMimeType(const std::string& path) const { +std::string VideoSource::GetMimeType(const std::string& path) { std::string mime_type; std::string ext = base::FilePath(path).Extension(); if (!ext.empty())
diff --git a/chrome/browser/ui/webui/chromeos/video_source.h b/chrome/browser/ui/webui/chromeos/video_source.h index 12a1a88..316509a 100644 --- a/chrome/browser/ui/webui/chromeos/video_source.h +++ b/chrome/browser/ui/webui/chromeos/video_source.h
@@ -28,13 +28,13 @@ ~VideoSource() override; // content::URLDataSource: - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& got_data_callback) override; - std::string GetMimeType(const std::string& path) const override; + std::string GetMimeType(const std::string& path) override; private: // Continuation from StartDataRequest().
diff --git a/chrome/browser/ui/webui/devtools_ui.cc b/chrome/browser/ui/webui/devtools_ui.cc index 0643144..a9cc1d8 100644 --- a/chrome/browser/ui/webui/devtools_ui.cc +++ b/chrome/browser/ui/webui/devtools_ui.cc
@@ -94,7 +94,7 @@ ~DevToolsDataSource() override = default; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, @@ -105,10 +105,10 @@ struct PendingRequest; // content::URLDataSource overrides. - std::string GetMimeType(const std::string& path) const override; - bool ShouldAddContentSecurityPolicy() const override; - bool ShouldDenyXFrameOptions() const override; - bool ShouldServeMimeTypeAsContentTypeHeader() const override; + std::string GetMimeType(const std::string& path) override; + bool ShouldAddContentSecurityPolicy() override; + bool ShouldDenyXFrameOptions() override; + bool ShouldServeMimeTypeAsContentTypeHeader() override; void OnLoadComplete(std::list<PendingRequest>::iterator request_iter, std::unique_ptr<std::string> response_body); @@ -158,7 +158,7 @@ DISALLOW_COPY_AND_ASSIGN(DevToolsDataSource); }; -std::string DevToolsDataSource::GetSource() const { +std::string DevToolsDataSource::GetSource() { return chrome::kChromeUIDevToolsHost; } @@ -235,19 +235,19 @@ callback.Run(NULL); } -std::string DevToolsDataSource::GetMimeType(const std::string& path) const { +std::string DevToolsDataSource::GetMimeType(const std::string& path) { return GetMimeTypeForPath(path); } -bool DevToolsDataSource::ShouldAddContentSecurityPolicy() const { +bool DevToolsDataSource::ShouldAddContentSecurityPolicy() { return false; } -bool DevToolsDataSource::ShouldDenyXFrameOptions() const { +bool DevToolsDataSource::ShouldDenyXFrameOptions() { return false; } -bool DevToolsDataSource::ShouldServeMimeTypeAsContentTypeHeader() const { +bool DevToolsDataSource::ShouldServeMimeTypeAsContentTypeHeader() { return true; }
diff --git a/chrome/browser/ui/webui/discards/DEPS b/chrome/browser/ui/webui/discards/DEPS new file mode 100644 index 0000000..f452a3a --- /dev/null +++ b/chrome/browser/ui/webui/discards/DEPS
@@ -0,0 +1,5 @@ +specific_include_rules = { + "discards_ui.h": [ + "+chrome/browser/performance_manager/webui_graph_dump.mojom.h", + ], +}
diff --git a/chrome/browser/ui/webui/extensions/extension_icon_source.cc b/chrome/browser/ui/webui/extensions/extension_icon_source.cc index eded33bb..618914d 100644 --- a/chrome/browser/ui/webui/extensions/extension_icon_source.cc +++ b/chrome/browser/ui/webui/extensions/extension_icon_source.cc
@@ -102,11 +102,11 @@ return ToBitmap(data, contents.length()); } -std::string ExtensionIconSource::GetSource() const { +std::string ExtensionIconSource::GetSource() { return chrome::kChromeUIExtensionIconHost; } -std::string ExtensionIconSource::GetMimeType(const std::string&) const { +std::string ExtensionIconSource::GetMimeType(const std::string&) { // We need to explicitly return a mime type, otherwise if the user tries to // drag the image they get no extension. return "image/png"; @@ -139,7 +139,7 @@ } } -bool ExtensionIconSource::AllowCaching() const { +bool ExtensionIconSource::AllowCaching() { // Should not be cached to reflect the latest contents that may be updated by // Extensions. return false;
diff --git a/chrome/browser/ui/webui/extensions/extension_icon_source.h b/chrome/browser/ui/webui/extensions/extension_icon_source.h index 62cee4a..c7be8dc8 100644 --- a/chrome/browser/ui/webui/extensions/extension_icon_source.h +++ b/chrome/browser/ui/webui/extensions/extension_icon_source.h
@@ -68,13 +68,13 @@ static SkBitmap* LoadImageByResourceId(int resource_id); // content::URLDataSource implementation. - std::string GetSource() const override; - std::string GetMimeType(const std::string&) const override; + std::string GetSource() override; + std::string GetMimeType(const std::string&) override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - bool AllowCaching() const override; + bool AllowCaching() override; private: // Encapsulates the request parameters for |request_id|.
diff --git a/chrome/browser/ui/webui/extensions/extensions_internals_source.cc b/chrome/browser/ui/webui/extensions/extensions_internals_source.cc index f7449ee..e7b36ba 100644 --- a/chrome/browser/ui/webui/extensions/extensions_internals_source.cc +++ b/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
@@ -277,12 +277,11 @@ ExtensionsInternalsSource::~ExtensionsInternalsSource() = default; -std::string ExtensionsInternalsSource::GetSource() const { +std::string ExtensionsInternalsSource::GetSource() { return chrome::kChromeUIExtensionsInternalsHost; } -std::string ExtensionsInternalsSource::GetMimeType( - const std::string& path) const { +std::string ExtensionsInternalsSource::GetMimeType(const std::string& path) { return "text/plain"; }
diff --git a/chrome/browser/ui/webui/extensions/extensions_internals_source.h b/chrome/browser/ui/webui/extensions/extensions_internals_source.h index c848a0a50..4a8a6ca 100644 --- a/chrome/browser/ui/webui/extensions/extensions_internals_source.h +++ b/chrome/browser/ui/webui/extensions/extensions_internals_source.h
@@ -18,8 +18,8 @@ ~ExtensionsInternalsSource() override; // content::URLDataSource: - std::string GetSource() const override; - std::string GetMimeType(const std::string& path) const override; + std::string GetSource() override; + std::string GetMimeType(const std::string& path) override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
diff --git a/chrome/browser/ui/webui/favicon_source.cc b/chrome/browser/ui/webui/favicon_source.cc index c93c9e6..111a732 100644 --- a/chrome/browser/ui/webui/favicon_source.cc +++ b/chrome/browser/ui/webui/favicon_source.cc
@@ -70,21 +70,17 @@ } // namespace FaviconSource::IconRequest::IconRequest() - : size_in_dip(gfx::kFaviconSize), - device_scale_factor(1.0f), - icon_request_origin(favicon::FaviconRequestOrigin::UNKNOWN) {} + : size_in_dip(gfx::kFaviconSize), device_scale_factor(1.0f) {} FaviconSource::IconRequest::IconRequest( const content::URLDataSource::GotDataCallback& cb, const GURL& path, int size, - float scale, - favicon::FaviconRequestOrigin origin) + float scale) : callback(cb), request_path(path), size_in_dip(size), - device_scale_factor(scale), - icon_request_origin(origin) {} + device_scale_factor(scale) {} FaviconSource::IconRequest::IconRequest(const IconRequest& other) = default; @@ -97,7 +93,7 @@ FaviconSource::~FaviconSource() { } -std::string FaviconSource::GetSource() const { +std::string FaviconSource::GetSource() { return chrome::kChromeUIFaviconHost; } @@ -141,10 +137,10 @@ // IconType. favicon_service->GetRawFavicon( url, favicon_base::IconType::kFavicon, desired_size_in_pixel, - base::BindRepeating( - &FaviconSource::OnFaviconDataAvailable, base::Unretained(this), - IconRequest(callback, url, parsed.size_in_dip, - parsed.device_scale_factor, unsafe_request_origin)), + base::BindRepeating(&FaviconSource::OnFaviconDataAvailable, + base::Unretained(this), + IconRequest(callback, url, parsed.size_in_dip, + parsed.device_scale_factor)), &cancelable_task_tracker_); } else { // Intercept requests for prepopulated pages if TopSites exists. @@ -167,10 +163,10 @@ GetOpenTabsUIDelegate(profile_); favicon_request_handler_.GetRawFaviconForPageURL( url, desired_size_in_pixel, - base::BindOnce( - &FaviconSource::OnFaviconDataAvailable, base::Unretained(this), - IconRequest(callback, url, parsed.size_in_dip, - parsed.device_scale_factor, unsafe_request_origin)), + base::BindOnce(&FaviconSource::OnFaviconDataAvailable, + base::Unretained(this), + IconRequest(callback, url, parsed.size_in_dip, + parsed.device_scale_factor)), unsafe_request_origin, favicon::FaviconRequestPlatform::kDesktop, favicon_service, LargeIconServiceFactory::GetForBrowserContext(profile_), @@ -181,17 +177,17 @@ } } -std::string FaviconSource::GetMimeType(const std::string&) const { +std::string FaviconSource::GetMimeType(const std::string&) { // We need to explicitly return a mime type, otherwise if the user tries to // drag the image they get no extension. return "image/png"; } -bool FaviconSource::AllowCaching() const { +bool FaviconSource::AllowCaching() { return false; } -bool FaviconSource::ShouldReplaceExistingSource() const { +bool FaviconSource::ShouldReplaceExistingSource() { // Leave the existing DataSource in place, otherwise we'll drop any pending // requests on the floor. return false; @@ -200,7 +196,7 @@ bool FaviconSource::ShouldServiceRequest( const GURL& url, content::ResourceContext* resource_context, - int render_process_id) const { + int render_process_id) { if (url.SchemeIs(chrome::kChromeSearchScheme)) { return InstantIOContext::ShouldServiceRequest(url, resource_context, render_process_id); @@ -226,8 +222,7 @@ void FaviconSource::SendDefaultResponse( const content::URLDataSource::GotDataCallback& callback) { - SendDefaultResponse(IconRequest(callback, GURL(), 16, 1.0f, - favicon::FaviconRequestOrigin::UNKNOWN)); + SendDefaultResponse(IconRequest(callback, GURL(), 16, 1.0f)); } void FaviconSource::SendDefaultResponse(const IconRequest& icon_request) {
diff --git a/chrome/browser/ui/webui/favicon_source.h b/chrome/browser/ui/webui/favicon_source.h index 2c725b7..6eec8e5b 100644 --- a/chrome/browser/ui/webui/favicon_source.h +++ b/chrome/browser/ui/webui/favicon_source.h
@@ -61,17 +61,17 @@ ~FaviconSource() override; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string&) const override; - bool AllowCaching() const override; - bool ShouldReplaceExistingSource() const override; + std::string GetMimeType(const std::string&) override; + bool AllowCaching() override; + bool ShouldReplaceExistingSource() override; bool ShouldServiceRequest(const GURL& url, content::ResourceContext* resource_context, - int render_process_id) const override; + int render_process_id) override; protected: struct IconRequest { @@ -79,8 +79,7 @@ IconRequest(const content::URLDataSource::GotDataCallback& cb, const GURL& path, int size, - float scale, - favicon::FaviconRequestOrigin origin); + float scale); IconRequest(const IconRequest& other); ~IconRequest(); @@ -88,7 +87,6 @@ GURL request_path; int size_in_dip; float device_scale_factor; - favicon::FaviconRequestOrigin icon_request_origin; }; // Exposed for testing.
diff --git a/chrome/browser/ui/webui/fileicon_source.cc b/chrome/browser/ui/webui/fileicon_source.cc index b38de38..7706c0f 100644 --- a/chrome/browser/ui/webui/fileicon_source.cc +++ b/chrome/browser/ui/webui/fileicon_source.cc
@@ -108,7 +108,7 @@ } } -std::string FileIconSource::GetSource() const { +std::string FileIconSource::GetSource() { return kFileIconPath; } @@ -123,12 +123,12 @@ FetchFileIcon(file_path, scale_factor, icon_size, callback); } -std::string FileIconSource::GetMimeType(const std::string&) const { +std::string FileIconSource::GetMimeType(const std::string&) { // Rely on image decoder inferring the correct type. return std::string(); } -bool FileIconSource::AllowCaching() const { +bool FileIconSource::AllowCaching() { return false; }
diff --git a/chrome/browser/ui/webui/fileicon_source.h b/chrome/browser/ui/webui/fileicon_source.h index b8c093c..38fd779 100644 --- a/chrome/browser/ui/webui/fileicon_source.h +++ b/chrome/browser/ui/webui/fileicon_source.h
@@ -25,13 +25,13 @@ ~FileIconSource() override; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string&) const override; - bool AllowCaching() const override; + std::string GetMimeType(const std::string&) override; + bool AllowCaching() override; protected: // Once the |path| and |icon_size| has been determined from the request, this
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc index d21cebf1..f60feb9 100644 --- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc +++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -84,11 +84,11 @@ ~InterstitialHTMLSource() override = default; // content::URLDataSource: - std::string GetMimeType(const std::string& mime_type) const override; - std::string GetSource() const override; - std::string GetContentSecurityPolicyScriptSrc() const override; - std::string GetContentSecurityPolicyStyleSrc() const override; - std::string GetContentSecurityPolicyImgSrc() const override; + std::string GetMimeType(const std::string& mime_type) override; + std::string GetSource() override; + std::string GetContentSecurityPolicyScriptSrc() override; + std::string GetContentSecurityPolicyStyleSrc() override; + std::string GetContentSecurityPolicyImgSrc() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, @@ -441,25 +441,24 @@ // InterstitialHTMLSource -std::string InterstitialHTMLSource::GetMimeType( - const std::string& mime_type) const { +std::string InterstitialHTMLSource::GetMimeType(const std::string& mime_type) { return "text/html"; } -std::string InterstitialHTMLSource::GetSource() const { +std::string InterstitialHTMLSource::GetSource() { return chrome::kChromeUIInterstitialHost; } -std::string InterstitialHTMLSource::GetContentSecurityPolicyScriptSrc() const { +std::string InterstitialHTMLSource::GetContentSecurityPolicyScriptSrc() { // 'unsafe-inline' is added to script-src. return "script-src chrome://resources 'self' 'unsafe-inline';"; } -std::string InterstitialHTMLSource::GetContentSecurityPolicyStyleSrc() const { +std::string InterstitialHTMLSource::GetContentSecurityPolicyStyleSrc() { return "style-src 'self' 'unsafe-inline';"; } -std::string InterstitialHTMLSource::GetContentSecurityPolicyImgSrc() const { +std::string InterstitialHTMLSource::GetContentSecurityPolicyImgSrc() { return "img-src data:;"; }
diff --git a/chrome/browser/ui/webui/managed_ui_handler_unittest.cc b/chrome/browser/ui/webui/managed_ui_handler_unittest.cc index 06fd62c..f9a4b1b 100644 --- a/chrome/browser/ui/webui/managed_ui_handler_unittest.cc +++ b/chrome/browser/ui/webui/managed_ui_handler_unittest.cc
@@ -4,12 +4,10 @@ #include "chrome/browser/ui/webui/managed_ui_handler.h" -#include "base/test/scoped_feature_list.h" #include "base/token.h" #include "base/values.h" #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/common/chrome_features.h" #include "chrome/test/base/testing_profile.h" #include "components/policy/core/common/mock_configuration_policy_provider.h" #include "components/policy/core/common/policy_service_impl.h" @@ -35,8 +33,6 @@ ManagedUIHandlerTest() : source_(content::TestWebUIDataSource::Create( base::Token::CreateRandom().ToString())) { - features_.InitAndEnableFeature(features::kShowManagedUi); - // Create a TestingProfile that uses our MockConfigurationPolicyProvider. policy_provider()->Init(); policy::PolicyServiceImpl::Providers providers = {policy_provider()}; @@ -80,7 +76,6 @@ private: content::TestBrowserThreadBundle bundle_; - base::test::ScopedFeatureList features_; testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_; std::unique_ptr<TestingProfile> profile_;
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc index a99d5b78ca..5cc53f9c7 100644 --- a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc +++ b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
@@ -83,7 +83,7 @@ void RegisterMessages() override; private: - network::mojom::NetworkContext* GetNetworkContext() const; + network::mojom::NetworkContext* GetNetworkContext(); // Calls g_browser.receive in the renderer, passing in |command| and |arg|. // If the renderer is displaying a log file, the message will be ignored. @@ -137,7 +137,7 @@ bool succeeded); #endif - const content::WebUI* web_ui_; + content::WebUI* web_ui_; DISALLOW_COPY_AND_ASSIGN(NetInternalsMessageHandler); }; @@ -503,8 +503,8 @@ } #endif // defined(OS_CHROMEOS) -network::mojom::NetworkContext* NetInternalsMessageHandler::GetNetworkContext() - const { +network::mojom::NetworkContext* +NetInternalsMessageHandler::GetNetworkContext() { return content::BrowserContext::GetDefaultStoragePartition( web_ui_->GetWebContents()->GetBrowserContext()) ->GetNetworkContext();
diff --git a/chrome/browser/ui/webui/ntp/new_tab_ui.cc b/chrome/browser/ui/webui/ntp/new_tab_ui.cc index f565648..cfef0e77 100644 --- a/chrome/browser/ui/webui/ntp/new_tab_ui.cc +++ b/chrome/browser/ui/webui/ntp/new_tab_ui.cc
@@ -169,7 +169,7 @@ : profile_(profile) { } -std::string NewTabUI::NewTabHTMLSource::GetSource() const { +std::string NewTabUI::NewTabHTMLSource::GetSource() { return chrome::kChromeUINewTabHost; } @@ -199,35 +199,31 @@ callback.Run(html_bytes.get()); } -std::string NewTabUI::NewTabHTMLSource::GetMimeType(const std::string& resource) - const { +std::string NewTabUI::NewTabHTMLSource::GetMimeType( + const std::string& resource) { return "text/html"; } -bool NewTabUI::NewTabHTMLSource::ShouldReplaceExistingSource() const { +bool NewTabUI::NewTabHTMLSource::ShouldReplaceExistingSource() { return false; } -std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyScriptSrc() - const { +std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyScriptSrc() { // 'unsafe-inline' and google resources are added to script-src. return "script-src chrome://resources 'self' 'unsafe-eval' 'unsafe-inline' " "*.google.com *.gstatic.com;"; } -std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyStyleSrc() - const { +std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyStyleSrc() { return "style-src 'self' chrome://resources 'unsafe-inline' chrome://theme;"; } -std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyImgSrc() - const { +std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyImgSrc() { return "img-src chrome-search://thumb chrome-search://thumb2 " "chrome-search://theme chrome://theme data:;"; } -std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyChildSrc() - const { +std::string NewTabUI::NewTabHTMLSource::GetContentSecurityPolicyChildSrc() { return "child-src chrome-search://most-visited;"; }
diff --git a/chrome/browser/ui/webui/ntp/new_tab_ui.h b/chrome/browser/ui/webui/ntp/new_tab_ui.h index 53f6473e..63d4d59 100644 --- a/chrome/browser/ui/webui/ntp/new_tab_ui.h +++ b/chrome/browser/ui/webui/ntp/new_tab_ui.h
@@ -55,17 +55,17 @@ ~NewTabHTMLSource() override; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string&) const override; - bool ShouldReplaceExistingSource() const override; - std::string GetContentSecurityPolicyScriptSrc() const override; - std::string GetContentSecurityPolicyStyleSrc() const override; - std::string GetContentSecurityPolicyImgSrc() const override; - std::string GetContentSecurityPolicyChildSrc() const override; + std::string GetMimeType(const std::string&) override; + bool ShouldReplaceExistingSource() override; + std::string GetContentSecurityPolicyScriptSrc() override; + std::string GetContentSecurityPolicyStyleSrc() override; + std::string GetContentSecurityPolicyImgSrc() override; + std::string GetContentSecurityPolicyChildSrc() override; private: // Pointer back to the original profile.
diff --git a/chrome/browser/ui/webui/prefs_internals_source.cc b/chrome/browser/ui/webui/prefs_internals_source.cc index 9d30d11..382f02f 100644 --- a/chrome/browser/ui/webui/prefs_internals_source.cc +++ b/chrome/browser/ui/webui/prefs_internals_source.cc
@@ -19,11 +19,11 @@ PrefsInternalsSource::~PrefsInternalsSource() = default; -std::string PrefsInternalsSource::GetSource() const { +std::string PrefsInternalsSource::GetSource() { return chrome::kChromeUIPrefsInternalsHost; } -std::string PrefsInternalsSource::GetMimeType(const std::string& path) const { +std::string PrefsInternalsSource::GetMimeType(const std::string& path) { return "text/plain"; }
diff --git a/chrome/browser/ui/webui/prefs_internals_source.h b/chrome/browser/ui/webui/prefs_internals_source.h index 471c05d..d20af55 100644 --- a/chrome/browser/ui/webui/prefs_internals_source.h +++ b/chrome/browser/ui/webui/prefs_internals_source.h
@@ -17,8 +17,8 @@ ~PrefsInternalsSource() override; // content::URLDataSource: - std::string GetSource() const override; - std::string GetMimeType(const std::string& path) const override; + std::string GetSource() override; + std::string GetMimeType(const std::string& path) override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc index 47528eac..a1ff2e4 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc +++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -284,6 +284,10 @@ const char kCloudPrintURL[] = "cloudPrintURL"; // Name of a dictionary field holding the signed in user accounts. const char kUserAccounts[] = "userAccounts"; +// Name of a dictionary field indicating whether sync is available. If false, +// Print Preview will always send a request to the Google Cloud Print server on +// load, to check the user's sign in state. +const char kSyncAvailable[] = "syncAvailable"; // Get the print job settings dictionary from |json_str|. // Returns |base::Value()| on failure. @@ -861,8 +865,19 @@ CHECK(args->GetBoolean(0, &add_account)); chrome::ScopedTabbedBrowserDisplayer displayer(Profile::FromWebUI(web_ui())); - print_dialog_cloud::CreateCloudPrintSigninTab(displayer.browser(), - add_account); + print_dialog_cloud::CreateCloudPrintSigninTab( + displayer.browser(), add_account, + base::BindOnce(&PrintPreviewHandler::OnSignInTabClosed, + weak_factory_.GetWeakPtr())); +} + +void PrintPreviewHandler::OnSignInTabClosed() { + if (identity_manager_) { + // Sign in state will be reported in OnAccountsInCookieJarUpdated, so no + // need to do anything here. + return; + } + FireWebUIListener("check-for-account-update"); } #if defined(OS_CHROMEOS) @@ -960,6 +975,9 @@ for (const gaia::ListedAccount& account : accounts) { account_list.GetList().emplace_back(account.email); } + settings->SetKey(kSyncAvailable, base::Value(true)); + } else { + settings->SetKey(kSyncAvailable, base::Value(false)); } settings->SetKey(kUserAccounts, std::move(account_list)); }
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h index 47856437f..1b34ac7 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h +++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -225,6 +225,9 @@ // a boolean indicating whether the user is adding an account. void HandleSignin(const base::ListValue* args); + // Called when the tab opened by HandleSignIn() is closed. + void OnSignInTabClosed(); + #if defined(OS_CHROMEOS) // Generates new token and sends back to UI. void HandleGetAccessToken(const base::ListValue* args);
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc index efbbb97..64af66f 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc +++ b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
@@ -376,6 +376,8 @@ settings->FindKeyOfType("cloudPrintURL", base::Value::Type::STRING)); ASSERT_TRUE( settings->FindKeyOfType("userAccounts", base::Value::Type::LIST)); + ASSERT_TRUE( + settings->FindKeyOfType("syncAvailable", base::Value::Type::BOOLEAN)); } IPC::TestSink& initiator_sink() {
diff --git a/chrome/browser/ui/webui/theme_source.cc b/chrome/browser/ui/webui/theme_source.cc index a1d4d244..927c4b9a 100644 --- a/chrome/browser/ui/webui/theme_source.cc +++ b/chrome/browser/ui/webui/theme_source.cc
@@ -73,7 +73,7 @@ ThemeSource::~ThemeSource() = default; -std::string ThemeSource::GetSource() const { +std::string ThemeSource::GetSource() { return chrome::kChromeUIThemeHost; } @@ -157,14 +157,14 @@ } } -std::string ThemeSource::GetMimeType(const std::string& path) const { +std::string ThemeSource::GetMimeType(const std::string& path) { std::string parsed_path; webui::ParsePathAndScale(GetThemeUrl(path), &parsed_path, nullptr); return IsNewTabCssPath(parsed_path) ? "text/css" : "image/png"; } scoped_refptr<base::SingleThreadTaskRunner> -ThemeSource::TaskRunnerForRequestPath(const std::string& path) const { +ThemeSource::TaskRunnerForRequestPath(const std::string& path) { std::string parsed_path; webui::ParsePathAndScale(GetThemeUrl(path), &parsed_path, nullptr); @@ -181,14 +181,14 @@ : nullptr; } -bool ThemeSource::AllowCaching() const { +bool ThemeSource::AllowCaching() { return false; } bool ThemeSource::ShouldServiceRequest( const GURL& url, content::ResourceContext* resource_context, - int render_process_id) const { + int render_process_id) { return url.SchemeIs(chrome::kChromeSearchScheme) ? InstantIOContext::ShouldServiceRequest(url, resource_context, render_process_id)
diff --git a/chrome/browser/ui/webui/theme_source.h b/chrome/browser/ui/webui/theme_source.h index 724e01f..aa323d6 100644 --- a/chrome/browser/ui/webui/theme_source.h +++ b/chrome/browser/ui/webui/theme_source.h
@@ -21,18 +21,18 @@ ~ThemeSource() override; // content::URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string& path) const override; + std::string GetMimeType(const std::string& path) override; scoped_refptr<base::SingleThreadTaskRunner> TaskRunnerForRequestPath( - const std::string& path) const override; - bool AllowCaching() const override; + const std::string& path) override; + bool AllowCaching() override; bool ShouldServiceRequest(const GURL& url, content::ResourceContext* resource_context, - int render_process_id) const override; + int render_process_id) override; private: // Fetches and sends the theme bitmap.
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc index 53148f2..e056112 100644 --- a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc +++ b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.cc
@@ -6,6 +6,7 @@ #include <stdint.h> +#include <algorithm> #include <utility> #include "base/bind.h" @@ -14,6 +15,7 @@ #include "base/macros.h" #include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" +#include "base/rand_util.h" #include "base/time/clock.h" #include "base/time/default_clock.h" #include "base/time/default_tick_clock.h" @@ -22,8 +24,10 @@ #include "chrome/common/pref_names.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/update_engine_client.h" +#include "chromeos/settings/timezone_settings.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" +#include "third_party/icu/source/i18n/unicode/timezone.h" using chromeos::DBusThreadManager; using chromeos::UpdateEngineClient; @@ -118,7 +122,6 @@ upgrade_notification_timer_(tick_clock), initialized_(false), weak_factory_(this) { - CalculateThresholds(); // Not all tests provide a PrefService for local_state(). PrefService* local_state = g_browser_process->local_state(); if (local_state) { @@ -157,14 +160,11 @@ } base::TimeDelta UpgradeDetectorChromeos::GetHighAnnoyanceLevelDelta() { - return high_threshold_ - elevated_threshold_; + return high_deadline_ - elevated_deadline_; } base::Time UpgradeDetectorChromeos::GetHighAnnoyanceDeadline() { - const base::Time detected_time = upgrade_detected_time(); - if (detected_time.is_null()) - return detected_time; - return detected_time + high_threshold_; + return high_deadline_; } // static @@ -183,15 +183,63 @@ return base::TimeDelta::FromMilliseconds(value); } -void UpgradeDetectorChromeos::CalculateThresholds() { +// static +base::TimeDelta UpgradeDetectorChromeos::GenRandomTimeDelta( + base::TimeDelta max) { + return max * base::RandDouble(); +} + +// static +base::Time UpgradeDetectorChromeos::AdjustDeadline(base::Time deadline) { + // Compute the offset applied to GMT to get local time at |deadline|. + const icu::TimeZone& time_zone = + chromeos::system::TimezoneSettings::GetInstance()->GetTimezone(); + UErrorCode status = U_ZERO_ERROR; + int32_t raw_offset, dst_offset; + time_zone.getOffset(deadline.ToDoubleT() * base::Time::kMillisecondsPerSecond, + true /* local */, raw_offset, dst_offset, status); + base::TimeDelta time_zone_offset; + if (U_FAILURE(status)) { + LOG(ERROR) << "Failed to get time zone offset, error code: " << status; + // The fallback case is to get the raw timezone offset ignoring the daylight + // saving time. + time_zone_offset = + base::TimeDelta::FromMilliseconds(time_zone.getRawOffset()); + } else { + time_zone_offset = + base::TimeDelta::FromMilliseconds(raw_offset + dst_offset); + } + + // To get local midnight add timezone offset to deadline and treat this time + // as UTC based to use UTCMidnight(), then subtract timezone offset. + auto midnight = + (deadline + time_zone_offset).UTCMidnight() - time_zone_offset; + const auto day_time = deadline - midnight; + // Return the exact deadline if it naturally falls between 2am and 4am. + if (day_time >= base::TimeDelta::FromHours(2) && + day_time <= base::TimeDelta::FromHours(4)) { + return deadline; + } + // Advance to the next day if the deadline falls after 4am. + if (day_time > base::TimeDelta::FromHours(4)) + midnight += base::TimeDelta::FromDays(1); + + return midnight + base::TimeDelta::FromHours(2) + + GenRandomTimeDelta(base::TimeDelta::FromHours(2)); +} + +void UpgradeDetectorChromeos::CalculateDeadlines() { base::TimeDelta notification_period = GetRelaunchNotificationPeriod(); - high_threshold_ = notification_period.is_zero() ? kDefaultHighThreshold - : notification_period; + if (notification_period.is_zero()) + notification_period = kDefaultHighThreshold; + high_deadline_ = + AdjustDeadline(upgrade_detected_time() + notification_period); + base::TimeDelta heads_up_period = GetRelaunchHeadsUpPeriod(); if (heads_up_period.is_zero()) heads_up_period = kDefaultHeadsUpPeriod; - elevated_threshold_ = - high_threshold_ - std::min(heads_up_period, high_threshold_); + elevated_deadline_ = + std::max(high_deadline_ - heads_up_period, upgrade_detected_time()); } void UpgradeDetectorChromeos::OnRelaunchHeadsUpPeriodPrefChanged() { @@ -215,8 +263,10 @@ void UpgradeDetectorChromeos::UpdateStatusChanged( const UpdateEngineClient::Status& status) { if (status.status == UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) { - if (upgrade_detected_time().is_null()) + if (upgrade_detected_time().is_null()) { set_upgrade_detected_time(clock()->Now()); + CalculateDeadlines(); + } if (status.is_rollback) { // Powerwash will be required, determine what kind of notification to show @@ -246,32 +296,33 @@ void UpgradeDetectorChromeos::OnThresholdPrefChanged() { // Check the current stage and potentially notify observers now if a change to // the observed policies results in changes to the thresholds. - const base::TimeDelta old_elevated_threshold = elevated_threshold_; - const base::TimeDelta old_high_threshold = high_threshold_; - CalculateThresholds(); - if (!upgrade_detected_time().is_null() && - (elevated_threshold_ != old_elevated_threshold || - high_threshold_ != old_high_threshold)) { + if (upgrade_detected_time().is_null()) + return; + const base::Time old_elevated_deadline = elevated_deadline_; + const base::Time old_high_deadline = high_deadline_; + CalculateDeadlines(); + if (elevated_deadline_ != old_elevated_deadline || + high_deadline_ != old_high_deadline) { NotifyOnUpgrade(); } } void UpgradeDetectorChromeos::NotifyOnUpgrade() { - const base::TimeDelta delta = clock()->Now() - upgrade_detected_time(); + const base::Time current_time = clock()->Now(); // The delay from now until the next highest notification stage is reached, or // zero if the highest notification stage has been reached. base::TimeDelta next_delay; const auto last_stage = upgrade_notification_stage(); // These if statements must be sorted (highest interval first). - if (delta >= high_threshold_) { + if (current_time >= high_deadline_) { set_upgrade_notification_stage(UPGRADE_ANNOYANCE_HIGH); - } else if (delta >= elevated_threshold_) { + } else if (current_time >= elevated_deadline_) { set_upgrade_notification_stage(UPGRADE_ANNOYANCE_ELEVATED); - next_delay = high_threshold_ - delta; + next_delay = high_deadline_ - current_time; } else { set_upgrade_notification_stage(UPGRADE_ANNOYANCE_NONE); - next_delay = elevated_threshold_ - delta; + next_delay = elevated_deadline_ - current_time; } const auto new_stage = upgrade_notification_stage();
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h index e2e9885a..a87323d 100644 --- a/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h +++ b/chrome/browser/upgrade_detector/upgrade_detector_chromeos.h
@@ -49,24 +49,29 @@ UpgradeDetectorChromeos(const base::Clock* clock, const base::TickClock* tick_clock); + // Return adjusted high annoyance deadline which takes place at night between + // 2am and 4am. If |deadline| takes place after 4am it is prolonged for the + // next day night between 2am and 4am. + static base::Time AdjustDeadline(base::Time deadline); + private: friend class base::NoDestructor<UpgradeDetectorChromeos>; + // Return random TimeDelta uniformly selected between zero and |max|. + static base::TimeDelta GenRandomTimeDelta(base::TimeDelta max); + // Returns the period between first notification and Recommended / Required // deadline specified via the RelaunchHeadsUpPeriod policy setting, or a // zero delta if unset or out of range. static base::TimeDelta GetRelaunchHeadsUpPeriod(); - // Calculates |elevated_threshold_| and |high_threshold_|. - void CalculateThresholds(); + // Calculates |elevated_deadline_| and |high_deadline_|. + void CalculateDeadlines(); // Handles a change to the browser.relaunch_heads_up_period Local State // preference. Calls NotifyUpgrade if an upgrade is available. void OnRelaunchHeadsUpPeriodPrefChanged(); - // Returns the threshold to reach high annoyance level. - static base::TimeDelta DetermineHighThreshold(); - // UpgradeDetector: void OnRelaunchNotificationPeriodPrefChanged() override; @@ -86,11 +91,11 @@ void OnChannelsReceived(std::string current_channel, std::string target_channel); - // The delta from upgrade detection until elevated annoyance level is reached. - base::TimeDelta elevated_threshold_; + // The time when elevated annoyance deadline is reached. + base::Time elevated_deadline_; - // The delta from upgrade detection until high annoyance level is reached. - base::TimeDelta high_threshold_; + // The time when high annoyance deadline is reached. + base::Time high_deadline_; // Observes changes to the browser.relaunch_heads_up_period Local State // preference.
diff --git a/chrome/browser/upgrade_detector/upgrade_detector_chromeos_unittest.cc b/chrome/browser/upgrade_detector/upgrade_detector_chromeos_unittest.cc index 8c29afe..1b5860f 100644 --- a/chrome/browser/upgrade_detector/upgrade_detector_chromeos_unittest.cc +++ b/chrome/browser/upgrade_detector/upgrade_detector_chromeos_unittest.cc
@@ -20,9 +20,11 @@ #include "chrome/test/base/testing_browser_process.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_update_engine_client.h" +#include "chromeos/settings/timezone_settings.h" #include "components/prefs/testing_pref_service.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/icu/source/i18n/unicode/timezone.h" namespace { @@ -34,6 +36,7 @@ ~TestUpgradeDetectorChromeos() override = default; // Exposed for testing. + using UpgradeDetectorChromeos::AdjustDeadline; using UpgradeDetectorChromeos::UPGRADE_AVAILABLE_REGULAR; DISALLOW_COPY_AND_ASSIGN(TestUpgradeDetectorChromeos); @@ -63,7 +66,8 @@ class UpgradeDetectorChromeosTest : public ::testing::Test { protected: UpgradeDetectorChromeosTest() - : scoped_task_environment_( + : utc_(icu::TimeZone::createTimeZone("Etc/GMT")), + scoped_task_environment_( base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME), scoped_local_state_(TestingBrowserProcess::GetGlobal()) { // Disable the detector's check to see if autoupdates are inabled. @@ -78,6 +82,10 @@ dbus_setter->SetUpdateEngineClient( std::unique_ptr<chromeos::UpdateEngineClient>( fake_update_engine_client_)); + // Set UTC timezone + chromeos::system::TimezoneSettings::GetInstance()->SetTimezone(*utc_); + // Fast forward to align deadline be at 2am + FastForwardBy(base::TimeDelta::FromHours(2)); } const base::Clock* GetMockClock() { @@ -132,6 +140,7 @@ } private: + std::unique_ptr<icu::TimeZone> utc_; base::test::ScopedTaskEnvironment scoped_task_environment_; ScopedTestingLocalState scoped_local_state_; @@ -359,3 +368,59 @@ upgrade_detector.Shutdown(); RunUntilIdle(); } + +TEST_F(UpgradeDetectorChromeosTest, TimezoneAdjustment) { + TestUpgradeDetectorChromeos upgrade_detector(GetMockClock(), + GetMockTickClock()); + upgrade_detector.Init(); + const auto delta = base::TimeDelta::FromDays(7); + + // Europe/Moscow timezone + std::unique_ptr<icu::TimeZone> msk_timezone( + icu::TimeZone::createTimeZone("Europe/Moscow")); + chromeos::system::TimezoneSettings::GetInstance()->SetTimezone(*msk_timezone); + base::Time detect_time; + ASSERT_TRUE( + base::Time::FromString("1 Jan 2018 06:00 UTC+0300", &detect_time)); + base::Time deadline, deadline_lower_border, deadline_upper_border; + ASSERT_TRUE(base::Time::FromString("9 Jan 2018 02:00 UTC+0300", + &deadline_lower_border)); + ASSERT_TRUE(base::Time::FromString("9 Jan 2018 04:00 UTC+0300", + &deadline_upper_border)); + deadline = upgrade_detector.AdjustDeadline(detect_time + delta); + EXPECT_GE(deadline, deadline_lower_border); + EXPECT_LE(deadline, deadline_upper_border); + + // Pacific/Midway timezone + std::unique_ptr<icu::TimeZone> midway_timezone( + icu::TimeZone::createTimeZone("Pacific/Midway")); + chromeos::system::TimezoneSettings::GetInstance()->SetTimezone( + *midway_timezone); + ASSERT_TRUE( + base::Time::FromString("1 Jan 2018 23:00 UTC-1100", &detect_time)); + ASSERT_TRUE(base::Time::FromString("9 Jan 2018 02:00 UTC-1100", + &deadline_lower_border)); + ASSERT_TRUE(base::Time::FromString("9 Jan 2018 04:00 UTC-1100", + &deadline_upper_border)); + deadline = upgrade_detector.AdjustDeadline(detect_time + delta); + EXPECT_GE(deadline, deadline_lower_border); + EXPECT_LE(deadline, deadline_upper_border); + + // Pacific/Kiritimati timezone + std::unique_ptr<icu::TimeZone> kiritimati_timezone( + icu::TimeZone::createTimeZone("Pacific/Kiritimati")); + chromeos::system::TimezoneSettings::GetInstance()->SetTimezone( + *kiritimati_timezone); + ASSERT_TRUE( + base::Time::FromString("1 Jan 2018 16:30 UTC+1400", &detect_time)); + ASSERT_TRUE(base::Time::FromString("9 Jan 2018 02:00 UTC+1400", + &deadline_lower_border)); + ASSERT_TRUE(base::Time::FromString("9 Jan 2018 04:00 UTC+1400", + &deadline_upper_border)); + deadline = upgrade_detector.AdjustDeadline(detect_time + delta); + EXPECT_GE(deadline, deadline_lower_border); + EXPECT_LE(deadline, deadline_upper_border); + + upgrade_detector.Shutdown(); + RunUntilIdle(); +}
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn index 81c9570..0d6a3538 100644 --- a/chrome/common/BUILD.gn +++ b/chrome/common/BUILD.gn
@@ -242,6 +242,7 @@ "//components/gcm_driver/common", "//components/metrics", "//components/metrics:net", + "//components/metrics/public/interfaces:call_stack_mojo_bindings", "//components/nacl/common:buildflags", "//components/nacl/common:process_type", "//components/nacl/common:switches",
diff --git a/chrome/common/DEPS b/chrome/common/DEPS index 4d4b7b05..5248be4 100644 --- a/chrome/common/DEPS +++ b/chrome/common/DEPS
@@ -26,6 +26,7 @@ "+components/metrics/client_info.h", "+components/metrics/metadata_recorder.h", "+components/metrics/metrics_pref_names.h", + "+components/metrics/public", "+components/nacl/common", "+components/net_log", "+components/network_session_configurator/common",
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index d39a1fa..188efd4f 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -376,6 +376,11 @@ }; #endif // !defined(OS_ANDROID) +#if defined(OS_CHROMEOS) +const base::Feature kKernelnextVMs{"KernelnextVMs", + base::FEATURE_DISABLED_BY_DEFAULT}; +#endif + // Uses KidsManagement UrlClassification instead of SafeSearch for supervised // accounts. const base::Feature kKidsManagementUrlClassification{ @@ -535,11 +540,6 @@ const base::Feature kSecurityKeyAttestationPrompt{ "SecurityKeyAttestationPrompt", base::FEATURE_ENABLED_BY_DEFAULT}; -#if !defined(OS_ANDROID) -const base::Feature kShowManagedUi{"ShowManagedUi", - base::FEATURE_ENABLED_BY_DEFAULT}; -#endif - #if defined(OS_ANDROID) const base::Feature kShowTrustedPublisherURL{"ShowTrustedPublisherURL", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index 904bbe0..dd0b3bd 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -240,6 +240,11 @@ extern const base::Feature kIntentPicker; #endif +#if defined(OS_CHROMEOS) +COMPONENT_EXPORT(CHROME_FEATURES) +extern const base::Feature kKernelnextVMs; +#endif + COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kKidsManagementUrlClassification; @@ -348,10 +353,6 @@ COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kSecurityKeyAttestationPrompt; -#if !defined(OS_ANDROID) -COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kShowManagedUi; -#endif - #if defined(OS_ANDROID) COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kShowTrustedPublisherURL;
diff --git a/chrome/common/thread_profiler.cc b/chrome/common/thread_profiler.cc index 8f8f783..5b480c8 100644 --- a/chrome/common/thread_profiler.cc +++ b/chrome/common/thread_profiler.cc
@@ -22,7 +22,6 @@ #include "content/public/common/content_switches.h" #include "content/public/common/service_names.mojom.h" #include "services/service_manager/embedder/switches.h" -#include "services/service_manager/public/cpp/connector.h" using CallStackProfileBuilder = metrics::CallStackProfileBuilder; using CallStackProfileParams = metrics::CallStackProfileParams; @@ -187,18 +186,14 @@ } // static -void ThreadProfiler::SetServiceManagerConnectorForChildProcess( - service_manager::Connector* connector) { +void ThreadProfiler::SetCollectorForChildProcess( + mojo::PendingRemote<metrics::mojom::CallStackProfileCollector> collector) { if (!StackSamplingConfiguration::Get()->IsProfilerEnabledForCurrentProcess()) return; DCHECK_NE(CallStackProfileParams::BROWSER_PROCESS, GetProcess()); - - metrics::mojom::CallStackProfileCollectorPtr browser_interface; - connector->BindInterface(content::mojom::kSystemServiceName, - &browser_interface); CallStackProfileBuilder::SetParentProfileCollectorForChildProcess( - std::move(browser_interface)); + metrics::mojom::CallStackProfileCollectorPtr(std::move(collector))); } // ThreadProfiler implementation synopsis:
diff --git a/chrome/common/thread_profiler.h b/chrome/common/thread_profiler.h index 4abf2e2..c1793e70 100644 --- a/chrome/common/thread_profiler.h +++ b/chrome/common/thread_profiler.h
@@ -16,12 +16,10 @@ #include "base/threading/thread_checker.h" #include "base/time/time.h" #include "components/metrics/call_stack_profile_params.h" +#include "components/metrics/public/interfaces/call_stack_profile_collector.mojom.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "third_party/metrics_proto/sampled_profile.pb.h" -namespace service_manager { -class Connector; -} - // PeriodicSamplingScheduler repeatedly schedules periodic sampling of the // thread through calls to GetTimeToNextCollection(). This class is exposed // to allow testing. @@ -95,8 +93,8 @@ // exposed to the child process, and metrics::mojom::CallStackProfileCollector // declared in chrome_content_browser_manifest_overlay.json, for the binding // to succeed. - static void SetServiceManagerConnectorForChildProcess( - service_manager::Connector* connector); + static void SetCollectorForChildProcess( + mojo::PendingRemote<metrics::mojom::CallStackProfileCollector> collector); private: class WorkIdRecorder;
diff --git a/chrome/gpu/chrome_content_gpu_client.cc b/chrome/gpu/chrome_content_gpu_client.cc index 02e80a8f..9e320e5 100644 --- a/chrome/gpu/chrome_content_gpu_client.cc +++ b/chrome/gpu/chrome_content_gpu_client.cc
@@ -14,7 +14,9 @@ #include "build/build_config.h" #include "content/public/child/child_thread.h" #include "content/public/common/content_switches.h" +#include "content/public/common/service_names.mojom.h" #include "mojo/public/cpp/bindings/strong_binding.h" +#include "services/service_manager/public/cpp/connector.h" #if BUILDFLAG(ENABLE_LIBRARY_CDMS) #include "media/cdm/cdm_paths.h" @@ -90,8 +92,12 @@ switches::kInProcessGPU)) { main_thread_profiler_->SetMainThreadTaskRunner( base::ThreadTaskRunnerHandle::Get()); - ThreadProfiler::SetServiceManagerConnectorForChildProcess( - content::ChildThread::Get()->GetConnector()); + + mojo::PendingRemote<metrics::mojom::CallStackProfileCollector> collector; + content::ChildThread::Get()->GetConnector()->Connect( + content::mojom::kSystemServiceName, + collector.InitWithNewPipeAndPassReceiver()); + ThreadProfiler::SetCollectorForChildProcess(std::move(collector)); } }
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index 4f04b84..bb33d93 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -435,8 +435,15 @@ switches::kSingleProcess)) { main_thread_profiler_->SetMainThreadTaskRunner( base::ThreadTaskRunnerHandle::Get()); - ThreadProfiler::SetServiceManagerConnectorForChildProcess( - thread->GetConnector()); + mojo::PendingRemote<metrics::mojom::CallStackProfileCollector> collector; + service_manager::Connector* connector = nullptr; + if (content::ChildThread::Get()) + connector = content::ChildThread::Get()->GetConnector(); + if (connector) { + connector->Connect(content::mojom::kBrowserServiceName, + collector.InitWithNewPipeAndPassReceiver()); + ThreadProfiler::SetCollectorForChildProcess(std::move(collector)); + } } }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 8a74cfb2..6685d0c6 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -464,18 +464,35 @@ android_manifest_dep = ":android_browsertests_manifest" deps = [ + ":android_browsertests_assets", ":android_browsertests_java", + ":browser_tests_runner", ":test_support", ":test_support_ui_android", "//chrome:chrome_android_core", "//chrome/android:app_hooks_java", "//chrome/android:chrome_java", + + # TODO(crbug.com/961849): This is needed for ShellManager which is what + # the ChromeBrowserTestsActivity is using to build the java UI. It's + # likely we want to replace it with something new that uses ChromeWindow + # and CompositorViewHolder instead. + "//content/shell:content_shell_lib", "//content/test:android_test_message_pump_support", + "//testing/android/native_test:native_test_support", ] sources = [ "android/browsertests_apk/android_browsertests_jni_onload.cc", - # TODO(crbug.com/961849): Write an Android browser test and put it here. + "base/android/android_browser_test_browsertest_android.cc", + ] + + data = [ + "$root_gen_dir/chrome/android/chrome_apk_paks/chrome_100_percent.pak", + "$root_gen_dir/chrome/android/chrome_apk_paks/locales/en-US.pak", + "$root_gen_dir/chrome/android/chrome_apk_paks/resources.pak", + "$root_gen_dir/components/components_resources.pak", + "$root_out_dir/browser_tests.pak", ] } @@ -484,16 +501,46 @@ testonly = true sources = [ - # TODO(crbug.com/961849): Write an AndroidBrowserTest base class and put - # it here. + "base/android/android_browser_test.cc", + "base/android/android_browser_test.h", ] - public_deps = [] + public_deps = [ + "//content/test:test_support", + ] deps = [ "//chrome/browser", "//content/public/browser", ] } + android_assets("android_browsertests_assets") { + testonly = true + sources = [] + deps = [] + + # These are grit() rules so they are in $root_gen_dir. + deps += [ + "//chrome/android:chrome_apk_paks", + "//components/resources", + ] + sources += [ + "$root_gen_dir/chrome/android/chrome_apk_paks/chrome_100_percent.pak", + "$root_gen_dir/chrome/android/chrome_apk_paks/locales/en-US.pak", + "$root_gen_dir/chrome/android/chrome_apk_paks/resources.pak", + "$root_gen_dir/components/components_resources.pak", + ] + + # These are repack() rules so they are in $root_out_dir. + deps += [ "//chrome:browser_tests_pak" ] + sources += [ "$root_out_dir/browser_tests.pak" ] + + if (use_v8_context_snapshot) { + deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_assets" ] + } else { + deps += [ "//v8:v8_external_startup_data_assets" ] + } + } + android_library("android_browsertests_java") { testonly = true deps = [ @@ -501,6 +548,7 @@ "//base:base_java", "//base:base_java_test_support", "//chrome/android:chrome_java", + "//content/public/test/android:android_test_message_pump_support_java", "//content/shell/android:content_shell_browsertests_java", "//testing/android/native_test:native_test_java", ] @@ -524,26 +572,26 @@ } } -if (!is_android) { - static_library("browser_tests_runner") { - testonly = true - sources = [] +static_library("browser_tests_runner") { + testonly = true + sources = [] - deps = [ - ":test_support", - ] + deps = [ + ":test_support", + ] - if (is_chromeos) { - sources += [ "base/browser_tests_main_chromeos.cc" ] - } else { - sources += [ "base/browser_tests_main.cc" ] - } - - if (is_win) { - deps += [ "//chrome/installer/util:strings" ] - } + if (is_chromeos) { + sources += [ "base/browser_tests_main_chromeos.cc" ] + } else { + sources += [ "base/browser_tests_main.cc" ] } + if (is_win) { + deps += [ "//chrome/installer/util:strings" ] + } +} + +if (!is_android) { test("browser_tests") { configs += [ "//build/config:precompiled_headers" ] defines = [ @@ -604,7 +652,6 @@ "//mojo/public/cpp/system", "//net", "//net:test_support", - "//net:test_support", "//ppapi/buildflags", "//printing/buildflags", "//rlz/buildflags",
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn index 0c5233f..56417e08 100644 --- a/chrome/test/android/BUILD.gn +++ b/chrome/test/android/BUILD.gn
@@ -73,7 +73,7 @@ "javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java", "javatests/src/org/chromium/chrome/test/util/browser/suggestions/ContentSuggestionsTestUtils.java", "javatests/src/org/chromium/chrome/test/util/browser/suggestions/DummySuggestionsEventReporter.java", - "javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeMostVisitedSites.java", + "javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java", "javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeSuggestionsSource.java", "javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java", "javatests/src/org/chromium/chrome/test/util/browser/sync/SyncTestUtil.java",
diff --git a/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java b/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java index 9b198f9..7ee2d1a 100644 --- a/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java +++ b/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java
@@ -23,10 +23,7 @@ super.onCreate(savedInstanceState); appendCommandLineFlags( "--remote-debugging-socket-name android_browsertests_devtools_remote"); - } - @Override - protected void initializeBrowserProcess() { // TODO(danakj): This sets up some of the Chrome Java stuff. // AsyncInitializationActivity normally does this for the ChromeActivity. // We skip handlePostNativeStartup() for now, which runs child processes. @@ -37,6 +34,10 @@ // its ShellManager. BrowserParts parts = new EmptyBrowserParts() {}; ChromeBrowserInitializer.getInstance(getApplicationContext()).handlePreNativeStartup(parts); + } + + @Override + protected void initializeBrowserProcess() { super.initializeBrowserProcess(); }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/NewTabPageTestUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/NewTabPageTestUtils.java index d21bde2..ed3a9d11 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/NewTabPageTestUtils.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/NewTabPageTestUtils.java
@@ -14,9 +14,9 @@ import org.chromium.chrome.browser.ntp.NewTabPage; import org.chromium.chrome.browser.preferences.ChromePreferenceManager; import org.chromium.chrome.browser.suggestions.SiteSuggestion; -import org.chromium.chrome.browser.suggestions.TileSectionType; -import org.chromium.chrome.browser.suggestions.TileSource; -import org.chromium.chrome.browser.suggestions.TileTitleSource; +import org.chromium.chrome.browser.suggestions.tile.TileSectionType; +import org.chromium.chrome.browser.suggestions.tile.TileSource; +import org.chromium.chrome.browser.suggestions.tile.TileTitleSource; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.touchless.TouchlessDelegate; import org.chromium.chrome.browser.util.FeatureUtilities;
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeMostVisitedSites.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeMostVisitedSites.java deleted file mode 100644 index 51587b9..0000000 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/FakeMostVisitedSites.java +++ /dev/null
@@ -1,135 +0,0 @@ -// 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.chrome.test.util.browser.suggestions; - -import org.chromium.base.ThreadUtils; -import org.chromium.chrome.browser.suggestions.MostVisitedSites; -import org.chromium.chrome.browser.suggestions.SiteSuggestion; -import org.chromium.chrome.browser.suggestions.Tile; -import org.chromium.chrome.browser.suggestions.TileSectionType; -import org.chromium.chrome.browser.suggestions.TileSource; -import org.chromium.chrome.browser.suggestions.TileTitleSource; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -/** - * A fake implementation of MostVisitedSites that returns a fixed list of most visited sites. - * - * Once the observer is set (through {@link #setObserver(Observer, int)}), updates to the data must - * be made on the UI thread, as they can result in UI manipulations. - */ -public class FakeMostVisitedSites implements MostVisitedSites { - private final List<String> mBlacklistedUrls = new ArrayList<>(); - - private List<SiteSuggestion> mSites = new ArrayList<>(); - private Observer mObserver; - - @Override - public void destroy() {} - - @Override - public void setObserver(Observer observer, int numResults) { - mObserver = observer; - notifyTileSuggestionsAvailable(); - } - - @Override - public void addBlacklistedUrl(String url) { - mBlacklistedUrls.add(url); - } - - @Override - public void removeBlacklistedUrl(String url) { - mBlacklistedUrls.remove(url); - } - - @Override - public void recordPageImpression(int tilesCount) { - // Metrics are stubbed out. - } - - @Override - public void recordTileImpression(Tile tile) { - // Metrics are stubbed out. - } - - @Override - public void recordOpenedMostVisitedItem(Tile tile) { - // Metrics are stubbed out. - } - - /** @return Whether {@link #addBlacklistedUrl} has been called on the given URL. */ - public boolean isUrlBlacklisted(String url) { - return mBlacklistedUrls.contains(url); - } - - /** - * Sets new tile suggestion data. - * - * If there is an observer it will be notified and the call has to be made on the UI thread. - */ - public void setTileSuggestions(List<SiteSuggestion> suggestions) { - mSites = new ArrayList<>(suggestions); - notifyTileSuggestionsAvailable(); - } - - /** - * Sets new tile suggestion data. - * - * If there is an observer it will be notified and the call has to be made on the UI thread. - */ - public void setTileSuggestions(SiteSuggestion... suggestions) { - setTileSuggestions(Arrays.asList(suggestions)); - } - - /** - * Sets new tile suggestion data, generating dummy data for the missing properties. - * - * If there is an observer it will be notified and the call has to be made on the UI thread. - * - * @param urls The URLs of the site suggestions. - * @see #setTileSuggestions(SiteSuggestion[]) - */ - public void setTileSuggestions(String... urls) { - setTileSuggestions(createSiteSuggestions(urls)); - } - - /** @return An unmodifiable view of the current list of sites. */ - public List<SiteSuggestion> getCurrentSites() { - return Collections.unmodifiableList(mSites); - } - - public static List<SiteSuggestion> createSiteSuggestions(String... urls) { - List<SiteSuggestion> suggestions = new ArrayList<>(urls.length); - for (String url : urls) suggestions.add(createSiteSuggestion(url)); - return suggestions; - } - - public static SiteSuggestion createSiteSuggestion(String url) { - return createSiteSuggestion(url, url); - } - - public static SiteSuggestion createSiteSuggestion(String title, String url) { - return new SiteSuggestion(title, url, "", TileTitleSource.TITLE_TAG, TileSource.TOP_SITES, - TileSectionType.PERSONALIZED, new Date()); - } - - private void notifyTileSuggestionsAvailable() { - if (mObserver == null) return; - - // Notifying the observer usually results in view modifications, so this call should always - // happen on the UI thread. We assert we do it here to make detecting related mistakes in - // tests more easily. - // To make initialisation easier, we only enforce that once the observer is set, using it as - // a signal that the test started and this is not the setup anymore. - ThreadUtils.assertOnUiThread(); - - mObserver.onSiteSuggestionsAvailable(mSites); - } -}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java index 7f5a068..7460e3c6 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/SuggestionsDependenciesRule.java
@@ -13,9 +13,9 @@ import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.suggestions.MostVisitedSites; import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory; import org.chromium.chrome.browser.suggestions.SuggestionsEventReporter; +import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites; import org.chromium.chrome.browser.widget.ThumbnailProvider; /**
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java new file mode 100644 index 0000000..a9cd168 --- /dev/null +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java
@@ -0,0 +1,135 @@ +// 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.chrome.test.util.browser.suggestions.mostvisited; + +import org.chromium.base.ThreadUtils; +import org.chromium.chrome.browser.suggestions.SiteSuggestion; +import org.chromium.chrome.browser.suggestions.mostvisited.MostVisitedSites; +import org.chromium.chrome.browser.suggestions.tile.Tile; +import org.chromium.chrome.browser.suggestions.tile.TileSectionType; +import org.chromium.chrome.browser.suggestions.tile.TileSource; +import org.chromium.chrome.browser.suggestions.tile.TileTitleSource; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * A fake implementation of MostVisitedSites that returns a fixed list of most visited sites. + * + * Once the observer is set (through {@link #setObserver(Observer, int)}), updates to the data must + * be made on the UI thread, as they can result in UI manipulations. + */ +public class FakeMostVisitedSites implements MostVisitedSites { + private final List<String> mBlacklistedUrls = new ArrayList<>(); + + private List<SiteSuggestion> mSites = new ArrayList<>(); + private Observer mObserver; + + @Override + public void destroy() {} + + @Override + public void setObserver(Observer observer, int numResults) { + mObserver = observer; + notifyTileSuggestionsAvailable(); + } + + @Override + public void addBlacklistedUrl(String url) { + mBlacklistedUrls.add(url); + } + + @Override + public void removeBlacklistedUrl(String url) { + mBlacklistedUrls.remove(url); + } + + @Override + public void recordPageImpression(int tilesCount) { + // Metrics are stubbed out. + } + + @Override + public void recordTileImpression(Tile tile) { + // Metrics are stubbed out. + } + + @Override + public void recordOpenedMostVisitedItem(Tile tile) { + // Metrics are stubbed out. + } + + /** @return Whether {@link #addBlacklistedUrl} has been called on the given URL. */ + public boolean isUrlBlacklisted(String url) { + return mBlacklistedUrls.contains(url); + } + + /** + * Sets new tile suggestion data. + * + * If there is an observer it will be notified and the call has to be made on the UI thread. + */ + public void setTileSuggestions(List<SiteSuggestion> suggestions) { + mSites = new ArrayList<>(suggestions); + notifyTileSuggestionsAvailable(); + } + + /** + * Sets new tile suggestion data. + * + * If there is an observer it will be notified and the call has to be made on the UI thread. + */ + public void setTileSuggestions(SiteSuggestion... suggestions) { + setTileSuggestions(Arrays.asList(suggestions)); + } + + /** + * Sets new tile suggestion data, generating dummy data for the missing properties. + * + * If there is an observer it will be notified and the call has to be made on the UI thread. + * + * @param urls The URLs of the site suggestions. + * @see #setTileSuggestions(SiteSuggestion[]) + */ + public void setTileSuggestions(String... urls) { + setTileSuggestions(createSiteSuggestions(urls)); + } + + /** @return An unmodifiable view of the current list of sites. */ + public List<SiteSuggestion> getCurrentSites() { + return Collections.unmodifiableList(mSites); + } + + public static List<SiteSuggestion> createSiteSuggestions(String... urls) { + List<SiteSuggestion> suggestions = new ArrayList<>(urls.length); + for (String url : urls) suggestions.add(createSiteSuggestion(url)); + return suggestions; + } + + public static SiteSuggestion createSiteSuggestion(String url) { + return createSiteSuggestion(url, url); + } + + public static SiteSuggestion createSiteSuggestion(String title, String url) { + return new SiteSuggestion(title, url, "", TileTitleSource.TITLE_TAG, TileSource.TOP_SITES, + TileSectionType.PERSONALIZED, new Date()); + } + + private void notifyTileSuggestionsAvailable() { + if (mObserver == null) return; + + // Notifying the observer usually results in view modifications, so this call should always + // happen on the UI thread. We assert we do it here to make detecting related mistakes in + // tests more easily. + // To make initialisation easier, we only enforce that once the observer is set, using it as + // a signal that the test started and this is not the setup anymore. + ThreadUtils.assertOnUiThread(); + + mObserver.onSiteSuggestionsAvailable(mSites); + } +}
diff --git a/chrome/test/base/android/android_browser_test.cc b/chrome/test/base/android/android_browser_test.cc new file mode 100644 index 0000000..b7174ac --- /dev/null +++ b/chrome/test/base/android/android_browser_test.cc
@@ -0,0 +1,23 @@ +// Copyright 2019 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/test/base/android/android_browser_test.h" + +#include "content/public/test/test_utils.h" + +AndroidBrowserTest::AndroidBrowserTest() = default; +AndroidBrowserTest::~AndroidBrowserTest() = default; + +void AndroidBrowserTest::PreRunTestOnMainThread() { + // Pump startup related events. + content::RunAllPendingInMessageLoop(); +} + +void AndroidBrowserTest::PostRunTestOnMainThread() { + // Sometimes tests leave Quit tasks in the MessageLoop (for shame), so let's + // run all pending messages here to avoid preempting the QuitBrowsers tasks. + // TODO(https://crbug.com/922118): Remove this once it is no longer possible + // to post QuitCurrent* tasks. + content::RunAllPendingInMessageLoop(); +}
diff --git a/chrome/test/base/android/android_browser_test.h b/chrome/test/base/android/android_browser_test.h new file mode 100644 index 0000000..d3c4912a --- /dev/null +++ b/chrome/test/base/android/android_browser_test.h
@@ -0,0 +1,20 @@ +// Copyright 2019 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_TEST_BASE_ANDROID_ANDROID_BROWSER_TEST_H_ +#define CHROME_TEST_BASE_ANDROID_ANDROID_BROWSER_TEST_H_ + +#include "content/public/test/browser_test_base.h" + +class AndroidBrowserTest : public content::BrowserTestBase { + public: + AndroidBrowserTest(); + ~AndroidBrowserTest() override; + + // content::BrowserTestBase implementation. + void PreRunTestOnMainThread() override; + void PostRunTestOnMainThread() override; +}; + +#endif // CHROME_TEST_BASE_ANDROID_ANDROID_BROWSER_TEST_H_
diff --git a/chrome/test/base/android/android_browser_test_browsertest_android.cc b/chrome/test/base/android/android_browser_test_browsertest_android.cc new file mode 100644 index 0000000..cf2b90f --- /dev/null +++ b/chrome/test/base/android/android_browser_test_browsertest_android.cc
@@ -0,0 +1,8 @@ +// Copyright 2019 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/test/base/android/android_browser_test.h" +#include "content/public/test/browser_test.h" + +IN_PROC_BROWSER_TEST_F(AndroidBrowserTest, Smoke) {}
diff --git a/chrome/test/base/web_ui_browser_test.cc b/chrome/test/base/web_ui_browser_test.cc index 4d711723..5f476f3 100644 --- a/chrome/test/base/web_ui_browser_test.cc +++ b/chrome/test/base/web_ui_browser_test.cc
@@ -387,7 +387,7 @@ ~MockWebUIDataSource() override {} private: - std::string GetSource() const override { return "dummyurl"; } + std::string GetSource() override { return "dummyurl"; } void StartDataRequest( const std::string& path, @@ -399,14 +399,14 @@ callback.Run(response.get()); } - std::string GetMimeType(const std::string& path) const override { + std::string GetMimeType(const std::string& path) override { return "text/html"; } // Append 'unsave-eval' to the default script-src CSP policy, since it is // needed by some tests using chrome://dummyurl (because they depend on // Mock4JS, see crbug.com/844820). - std::string GetContentSecurityPolicyScriptSrc() const override { + std::string GetContentSecurityPolicyScriptSrc() override { return "script-src chrome://resources 'self' 'unsafe-eval';"; }
diff --git a/chrome/test/data/arc_graphics_tracing/trace_async_events.json b/chrome/test/data/arc_graphics_tracing/trace_async_events.json new file mode 100644 index 0000000..33b6346 --- /dev/null +++ b/chrome/test/data/arc_graphics_tracing/trace_async_events.json
@@ -0,0 +1,5 @@ +{ + "traceEvents":[], + "systemTraceEvents":" app1-14141 [001] ...1 1.100000: tracing_mark_write: S|1141|async1|1\n app2-14142 [001] ...1 1.200000: tracing_mark_write: S|1142|async2|2\n app1-14141 [001] ...1 1.300000: tracing_mark_write: F|1141|async1|1\n app2-14142 [001] ...1 1.400000: tracing_mark_write: F|1142|async2|2\n" +} +
diff --git a/chrome/test/data/downloads/multiple_a_download_x_origin_redirect_to_download.html b/chrome/test/data/downloads/multiple_a_download_x_origin_redirect_to_download.html new file mode 100644 index 0000000..6f84610 --- /dev/null +++ b/chrome/test/data/downloads/multiple_a_download_x_origin_redirect_to_download.html
@@ -0,0 +1,12 @@ +<!DOCTYPE html> +<body></body> +<script> +const url_params = new URLSearchParams(location.search); +let link = document.createElement('a'); +link.download = ''; +link.href = url_params.get('download_url'); +document.body.appendChild(link); +link.click(); +link.click(); +link.click(); +</script> \ No newline at end of file
diff --git a/chrome/test/data/downloads/redirect_x_origin_download.html b/chrome/test/data/downloads/redirect_x_origin_download.html new file mode 100644 index 0000000..c2c06de --- /dev/null +++ b/chrome/test/data/downloads/redirect_x_origin_download.html
@@ -0,0 +1 @@ +redirecting \ No newline at end of file
diff --git a/chrome/test/data/downloads/redirect_x_origin_download.html.mock-http-headers b/chrome/test/data/downloads/redirect_x_origin_download.html.mock-http-headers new file mode 100644 index 0000000..df083f9 --- /dev/null +++ b/chrome/test/data/downloads/redirect_x_origin_download.html.mock-http-headers
@@ -0,0 +1,2 @@ +HTTP/1.1 302 Moved +Location: http://www.a.com:{{PORT}}/downloads/empty.bin \ No newline at end of file
diff --git a/chrome/test/data/webui/print_preview/cloud_print_interface_stub.js b/chrome/test/data/webui/print_preview/cloud_print_interface_stub.js index bc7b6b9..5b22b48 100644 --- a/chrome/test/data/webui/print_preview/cloud_print_interface_stub.js +++ b/chrome/test/data/webui/print_preview/cloud_print_interface_stub.js
@@ -43,6 +43,20 @@ } /** + * Helper method to derive logged in users from the |cloudPrintersMap_|. + * @return {!Array<string>} The logged in user accounts. + */ + getUsers_() { + const users = []; + this.cloudPrintersMap_.forEach((printer, key) => { + if (!users.includes(printer.account)) { + users.push(printer.account); + } + }); + return users; + } + + /** * Dispatches a CloudPrintInterfaceEventType.SEARCH_DONE event with the * printers that have been set so far using setPrinter(). * @override @@ -50,6 +64,15 @@ search(account) { this.methodCalled('search', account); this.searchInProgress_ = true; + const users = this.getUsers_(); + const activeUser = users.includes(account) ? account : (users[0] || ''); + if (activeUser) { + this.eventTarget_.dispatchEvent(new CustomEvent( + cloudprint.CloudPrintInterfaceEventType.UPDATE_USERS, + {detail: {users: users, activeUser: activeUser}})); + this.initialized_ = true; + } + const printers = []; this.cloudPrintersMap_.forEach((value) => { if (value.account === account) { @@ -86,12 +109,7 @@ print_preview.createDestinationKey(printerId, origin, account)); if (!this.initialized_) { - const users = []; - this.cloudPrintersMap_.forEach((printer, key) => { - if (!users.includes(printer.account)) { - users.push(printer.account); - } - }); + const users = this.getUsers_(); const activeUser = users.includes(account) ? account : (users[0] || ''); if (activeUser) { this.eventTarget_.dispatchEvent(new CustomEvent(
diff --git a/chrome/test/data/webui/print_preview/destination_select_test.js b/chrome/test/data/webui/print_preview/destination_select_test.js index 5d91f14..414c4a6 100644 --- a/chrome/test/data/webui/print_preview/destination_select_test.js +++ b/chrome/test/data/webui/print_preview/destination_select_test.js
@@ -95,7 +95,7 @@ destinationSettings.init( initialSettings.printerName, initialSettings.serializedDefaultDestinationSelectionRulesStr, - initialSettings.userAccounts); + initialSettings.userAccounts, true /* syncAvailable */); destinationSettings.disabled = false; return opt_expectPrinterFailure ? Promise.resolve() : Promise.race([ nativeLayer.whenCalled('getPrinterCapabilities'), whenCapabilitiesReady
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.js b/chrome/test/data/webui/print_preview/destination_settings_test.js index 292c945..88d50a1 100644 --- a/chrome/test/data/webui/print_preview/destination_settings_test.js +++ b/chrome/test/data/webui/print_preview/destination_settings_test.js
@@ -85,7 +85,7 @@ destinationSettings.init( 'FooDevice' /* printerName */, '' /* serializedDefaultDestinationSelectionRulesStr */, - [] /* userAccounts */); + [] /* userAccounts */, true /* syncAvailable */); assertTrue(dropdown.hidden); return test_util @@ -169,7 +169,7 @@ destinationSettings.init( '' /* printerName */, '' /* serializedDefaultDestinationSelectionRulesStr */, - initialAccounts); + initialAccounts, true /* syncAvailable */); destinationSettings.state = print_preview.State.READY; destinationSettings.disabled = false; }
diff --git a/chrome/test/data/webui/print_preview/user_manager_test.js b/chrome/test/data/webui/print_preview/user_manager_test.js index 1b3abad..50a0159 100644 --- a/chrome/test/data/webui/print_preview/user_manager_test.js +++ b/chrome/test/data/webui/print_preview/user_manager_test.js
@@ -32,11 +32,6 @@ nativeLayer = new print_preview.NativeLayerStub(); print_preview.NativeLayer.setInstance(nativeLayer); cloudPrintInterface = new print_preview.CloudPrintInterfaceStub(); - // Set up a cloud printer for each account, so that search will work. - cloudPrintInterface.setPrinter( - print_preview_test_utils.getGoogleDriveDestination(account1)); - cloudPrintInterface.setPrinter( - print_preview_test_utils.getGoogleDriveDestination(account2)); userManager = document.createElement('print-preview-user-manager'); @@ -61,13 +56,19 @@ // Checks that initializing and updating user accounts works as expected. test('update users', function() { + // Set up a cloud printer for each account. + cloudPrintInterface.setPrinter( + print_preview_test_utils.getGoogleDriveDestination(account1)); + cloudPrintInterface.setPrinter( + print_preview_test_utils.getGoogleDriveDestination(account2)); + assertTrue(userManager.cloudPrintDisabled); userManager.setCloudPrintInterface(cloudPrintInterface); assertFalse(userManager.cloudPrintDisabled); assertEquals(undefined, userManager.activeUser); - userManager.initUserAccounts([]); + userManager.initUserAccounts([], true /* syncAvailable */); assertEquals('', userManager.activeUser); assertEquals(0, userManager.users.length); assertEquals(0, cloudPrintInterface.getCallCount('search')); @@ -97,9 +98,61 @@ assertEquals(4, cloudPrintInterface.getCallCount('search')); }); - test('update active user', function() { + // Checks that initializing and updating user accounts works as expected + // when sync is unavailable. + test('update users without sync', function() { + assertTrue(userManager.cloudPrintDisabled); + userManager.setCloudPrintInterface(cloudPrintInterface); - userManager.initUserAccounts([account1, account2]); + assertFalse(userManager.cloudPrintDisabled); + assertEquals(undefined, userManager.activeUser); + + userManager.initUserAccounts([], false /* syncAvailable */); + return cloudPrintInterface.whenCalled('printer') + .then(() => { + assertEquals(undefined, userManager.activeUser); + assertEquals(0, userManager.users.length); + assertEquals(0, cloudPrintInterface.getCallCount('search')); + // Need to check for the Google Drive printer by calling + // cloudPrintInterface.printer(), since sync is not available. + assertEquals(1, cloudPrintInterface.getCallCount('printer')); + + // Simulate signing into an account by setting a cloud printer for + // it and firing the 'check-for-account-update' listener. + // This should update the list of users and the active user and + // trigger a call to search. + cloudPrintInterface.setPrinter( + print_preview_test_utils.getGoogleDriveDestination(account1)); + cr.webUIListenerCallback('check-for-account-update'); + return cloudPrintInterface.whenCalled('search'); + }) + .then(() => { + assertEquals(account1, userManager.activeUser); + assertEquals(1, userManager.users.length); + assertEquals(1, cloudPrintInterface.getCallCount('search')); + + // Simulate signing in to a second account. + cloudPrintInterface.setPrinter( + print_preview_test_utils.getGoogleDriveDestination(account2)); + cr.webUIListenerCallback('check-for-account-update'); + return cloudPrintInterface.whenCalled('search'); + }) + .then(() => { + assertEquals(account1, userManager.activeUser); + assertEquals(2, userManager.users.length); + assertEquals(2, cloudPrintInterface.getCallCount('search')); + }); + }); + + test('update active user', function() { + // Set up a cloud printer for each account. + cloudPrintInterface.setPrinter( + print_preview_test_utils.getGoogleDriveDestination(account1)); + cloudPrintInterface.setPrinter( + print_preview_test_utils.getGoogleDriveDestination(account2)); + userManager.setCloudPrintInterface(cloudPrintInterface); + userManager.initUserAccounts( + [account1, account2], true /* syncAvailable */); assertFalse(userManager.cloudPrintDisabled); assertEquals(account1, userManager.activeUser); assertEquals(2, userManager.users.length);
diff --git a/chrome/test/data/webui/signin/.eslintrc.js b/chrome/test/data/webui/signin/.eslintrc.js new file mode 100644 index 0000000..cce1973 --- /dev/null +++ b/chrome/test/data/webui/signin/.eslintrc.js
@@ -0,0 +1,9 @@ +// Copyright 2019 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. + +module.exports = { + 'rules': { + 'no-var': 'error', + }, +};
diff --git a/chrome/test/data/webui/signin/signin_browsertest.js b/chrome/test/data/webui/signin/signin_browsertest.js index 49c483a6..6a6c899 100644 --- a/chrome/test/data/webui/signin/signin_browsertest.js +++ b/chrome/test/data/webui/signin/signin_browsertest.js
@@ -14,7 +14,8 @@ * chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation.html. * This has to be declared as a variable for TEST_F to find it correctly. */ -SigninSyncConfirmationTest = class extends PolymerTest { +// eslint-disable-next-line no-var +var SigninSyncConfirmationTest = class extends PolymerTest { /** @override */ get typedefCppFixture() { return 'SigninBrowserTest';
diff --git a/chrome/test/data/webui/signin/sync_confirmation_test.js b/chrome/test/data/webui/signin/sync_confirmation_test.js index e0fd1cb..cd766e5 100644 --- a/chrome/test/data/webui/signin/sync_confirmation_test.js +++ b/chrome/test/data/webui/signin/sync_confirmation_test.js
@@ -9,7 +9,7 @@ setup(function() { PolymerTest.clearBody(); app = document.createElement('sync-confirmation-app'); - var accountImageRequested = false; + let accountImageRequested = false; registerMessageCallback('accountImageRequest', this, function() { accountImageRequested = true; }); @@ -62,11 +62,8 @@ // button. test('recordConsentOnConfirm', function() { app.$$('#confirmButton').click(); - return browserProxy.whenCalled('confirm').then(function(arguments) { - assertEquals(2, arguments.length); - var description = arguments[0]; - var confirmation = arguments[1]; - + return browserProxy.whenCalled('confirm').then(function( + [description, confirmation]) { assertEquals( JSON.stringify(STANDARD_CONSENT_DESCRIPTION_TEXT), JSON.stringify(description)); @@ -78,11 +75,9 @@ // button. test('recordConsentOnSettingsLink', function() { app.$$('#settingsButton').click(); - return browserProxy.whenCalled('goToSettings').then(function(arguments) { - assertEquals(2, arguments.length); - var description = arguments[0]; - var confirmation = arguments[1]; - + return browserProxy.whenCalled('goToSettings').then(function([ + description, confirmation + ]) { assertEquals( JSON.stringify(STANDARD_CONSENT_DESCRIPTION_TEXT), JSON.stringify(description));
diff --git a/chromecast/media/base/media_codec_support.cc b/chromecast/media/base/media_codec_support.cc index ffa7ad1..e50dbfa 100644 --- a/chromecast/media/base/media_codec_support.cc +++ b/chromecast/media/base/media_codec_support.cc
@@ -50,11 +50,13 @@ case ::media::kCodecHEVC: return kCodecHEVC; case ::media::kCodecDolbyVision: - if (codec_profile == ::media::DOLBYVISION_PROFILE0) { + if (codec_profile == ::media::DOLBYVISION_PROFILE0 || + codec_profile == ::media::DOLBYVISION_PROFILE9) { return kCodecDolbyVisionH264; } else if (codec_profile == ::media::DOLBYVISION_PROFILE4 || codec_profile == ::media::DOLBYVISION_PROFILE5 || - codec_profile == ::media::DOLBYVISION_PROFILE7) { + codec_profile == ::media::DOLBYVISION_PROFILE7 || + codec_profile == ::media::DOLBYVISION_PROFILE8) { return kCodecDolbyVisionHEVC; } LOG(ERROR) << "Unsupported video codec profile " << codec_profile;
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index 123fde0..d15601bc 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -12242.0.0 \ No newline at end of file +12248.0.0 \ No newline at end of file
diff --git a/chromeos/OWNERS b/chromeos/OWNERS index 7f59483..b1fd1cf1 100644 --- a/chromeos/OWNERS +++ b/chromeos/OWNERS
@@ -9,7 +9,6 @@ achuith@chromium.org alemate@chromium.org -derat@chromium.org oshima@chromium.org rkc@chromium.org satorux@chromium.org
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc index dd487d3..59bbe8d 100644 --- a/chromeos/constants/chromeos_switches.cc +++ b/chromeos/constants/chromeos_switches.cc
@@ -407,6 +407,11 @@ const char kIgnoreUserProfileMappingForTests[] = "ignore-user-profile-mapping-for-tests"; +// If set, the Chrome settings will not expose the option to enable crostini +// unless the enable-experimental-kernel-vm-support flag is set in +// chrome://flags +const char kKernelnextRestrictVMs[] = "kernelnext-restrict-vms"; + // Enables Chrome-as-a-login-manager behavior. const char kLoginManager[] = "login-manager";
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h index 0456610..adb46302 100644 --- a/chromeos/constants/chromeos_switches.h +++ b/chromeos/constants/chromeos_switches.h
@@ -155,6 +155,7 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kHomedir[]; COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kIgnoreUserProfileMappingForTests[]; +COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kKernelnextRestrictVMs[]; COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginManager[]; COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginProfile[]; COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginUser[];
diff --git a/chromeos/dbus/OWNERS b/chromeos/dbus/OWNERS index 54cd6ce..c5198cd 100644 --- a/chromeos/dbus/OWNERS +++ b/chromeos/dbus/OWNERS
@@ -1,6 +1,5 @@ stevenjb@chromium.org hashimoto@chromium.org -derat@chromium.org per-file *smb_provider*=amistry@chromium.org per-file *smb_provider*=baileyberro@chromium.org
diff --git a/chromeos/dbus/power/OWNERS b/chromeos/dbus/power/OWNERS deleted file mode 100644 index 3c97e54..0000000 --- a/chromeos/dbus/power/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -derat@chromium.org
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h index e6cbe28..7e55d63 100644 --- a/chromeos/services/assistant/assistant_manager_service_impl.h +++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -9,10 +9,8 @@ #include <string> #include <vector> -#include "ash/public/cpp/assistant/default_voice_interaction_observer.h" #include "ash/public/interfaces/ash_message_center_controller.mojom.h" #include "ash/public/interfaces/assistant_controller.mojom.h" -#include "ash/public/interfaces/voice_interaction_controller.mojom.h" #include "base/synchronization/lock.h" #include "base/threading/thread.h" #include "chromeos/assistant/internal/action/cros_action_module.h"
diff --git a/chromeos/services/cellular_setup/ota_activator.cc b/chromeos/services/cellular_setup/ota_activator.cc index 9cf5dad..ffc55f7 100644 --- a/chromeos/services/cellular_setup/ota_activator.cc +++ b/chromeos/services/cellular_setup/ota_activator.cc
@@ -11,13 +11,16 @@ namespace cellular_setup { OtaActivator::OtaActivator(base::OnceClosure on_finished_callback) - : on_finished_callback_(std::move(on_finished_callback)) {} + : on_finished_callback_(std::move(on_finished_callback)), binding_(this) {} OtaActivator::~OtaActivator() = default; mojom::CarrierPortalHandlerPtr OtaActivator::GenerateInterfacePtr() { + // Only one InterfacePtr should be created per instance. + DCHECK(!binding_); + mojom::CarrierPortalHandlerPtr interface_ptr; - bindings_.AddBinding(this, mojo::MakeRequest(&interface_ptr)); + binding_.Bind(mojo::MakeRequest(&interface_ptr)); return interface_ptr; }
diff --git a/chromeos/services/cellular_setup/ota_activator.h b/chromeos/services/cellular_setup/ota_activator.h index 83d3ec3..6846a58 100644 --- a/chromeos/services/cellular_setup/ota_activator.h +++ b/chromeos/services/cellular_setup/ota_activator.h
@@ -8,7 +8,7 @@ #include "base/callback.h" #include "base/macros.h" #include "chromeos/services/cellular_setup/public/mojom/cellular_setup.mojom.h" -#include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/bindings/binding.h" namespace chromeos { @@ -23,6 +23,9 @@ public: ~OtaActivator() override; + // Generates an InterfacePtr bound to this instance. Only one InterfacePtr may + // be bound to a single OtaActivator instance, so this function can only be + // called once. mojom::CarrierPortalHandlerPtr GenerateInterfacePtr(); protected: @@ -31,7 +34,7 @@ void InvokeOnFinishedCallback(); base::OnceClosure on_finished_callback_; - mojo::BindingSet<mojom::CarrierPortalHandler> bindings_; + mojo::Binding<mojom::CarrierPortalHandler> binding_; DISALLOW_COPY_AND_ASSIGN(OtaActivator); };
diff --git a/components/arc/common/video_accelerator_struct_traits.cc b/components/arc/common/video_accelerator_struct_traits.cc index dfb8d49..f99d0c7 100644 --- a/components/arc/common/video_accelerator_struct_traits.cc +++ b/components/arc/common/video_accelerator_struct_traits.cc
@@ -42,12 +42,12 @@ CHECK_PROFILE_ENUM(HEVCPROFILE_MAIN10); CHECK_PROFILE_ENUM(HEVCPROFILE_MAIN_STILL_PICTURE); CHECK_PROFILE_ENUM(HEVCPROFILE_MAX); -CHECK_PROFILE_ENUM(DOLBYVISION_MIN); CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE0); CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE4); CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE5); CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE7); -CHECK_PROFILE_ENUM(DOLBYVISION_MAX); +CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE8); +CHECK_PROFILE_ENUM(DOLBYVISION_PROFILE9); CHECK_PROFILE_ENUM(THEORAPROFILE_MIN); CHECK_PROFILE_ENUM(THEORAPROFILE_ANY); CHECK_PROFILE_ENUM(THEORAPROFILE_MAX); @@ -96,6 +96,8 @@ case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE4: case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE5: case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE7: + case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE8: + case arc::mojom::VideoCodecProfile::DOLBYVISION_PROFILE9: case arc::mojom::VideoCodecProfile::THEORAPROFILE_ANY: case arc::mojom::VideoCodecProfile::AV1PROFILE_PROFILE_MAIN: case arc::mojom::VideoCodecProfile::AV1PROFILE_PROFILE_HIGH:
diff --git a/components/arc/common/video_common.mojom b/components/arc/common/video_common.mojom index 188a3509..27e4f86 100644 --- a/components/arc/common/video_common.mojom +++ b/components/arc/common/video_common.mojom
@@ -41,12 +41,10 @@ HEVCPROFILE_MAIN10 = 17, HEVCPROFILE_MAIN_STILL_PICTURE = 18, HEVCPROFILE_MAX = HEVCPROFILE_MAIN_STILL_PICTURE, - DOLBYVISION_MIN = 19, - DOLBYVISION_PROFILE0 = DOLBYVISION_MIN, + DOLBYVISION_PROFILE0 = 19, DOLBYVISION_PROFILE4 = 20, DOLBYVISION_PROFILE5 = 21, DOLBYVISION_PROFILE7 = 22, - DOLBYVISION_MAX = DOLBYVISION_PROFILE7, THEORAPROFILE_MIN = 23, THEORAPROFILE_ANY = THEORAPROFILE_MIN, THEORAPROFILE_MAX = THEORAPROFILE_ANY, @@ -55,7 +53,9 @@ AV1PROFILE_PROFILE_HIGH = 25, AV1PROFILE_PROFILE_PRO = 26, AV1PROFILE_MAX = AV1PROFILE_PROFILE_PRO, - VIDEO_CODEC_PROFILE_MAX = AV1PROFILE_PROFILE_PRO, + DOLBYVISION_PROFILE8 = 27, + DOLBYVISION_PROFILE9 = 28, + VIDEO_CODEC_PROFILE_MAX = DOLBYVISION_PROFILE9, }; [Extensible]
diff --git a/components/autofill/content/browser/risk/fingerprint.cc b/components/autofill/content/browser/risk/fingerprint.cc index 2e95a90..ab53f72 100644 --- a/components/autofill/content/browser/risk/fingerprint.cc +++ b/components/autofill/content/browser/risk/fingerprint.cc
@@ -163,7 +163,7 @@ // Writes info about the machine's GPU into the |machine|. void AddGpuInfoToFingerprint(Fingerprint::MachineCharacteristics* machine, - const content::GpuDataManager& gpu_data_manager) { + content::GpuDataManager& gpu_data_manager) { if (!gpu_data_manager.IsEssentialGpuInfoAvailable()) return;
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc index a88bf4b..2ac43a0 100644 --- a/components/autofill/core/browser/autofill_external_delegate.cc +++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -228,7 +228,8 @@ manager_->OnUserAcceptedCardsFromAccountOption(); } else { if (identifier > 0) // Denotes an Autofill suggestion. - AutofillMetrics::LogAutofillSuggestionAcceptedIndex(position); + AutofillMetrics::LogAutofillSuggestionAcceptedIndex(position, + popup_type_); FillAutofillFormData(identifier, false); }
diff --git a/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/components/autofill/core/browser/autofill_external_delegate_unittest.cc index afa25be6..c9d6c18 100644 --- a/components/autofill/core/browser/autofill_external_delegate_unittest.cc +++ b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -595,8 +595,7 @@ 0); } -// Test that an accepted autofill suggestion will fill the form and log the -// proper metric. +// Test that an accepted autofill suggestion will fill the form. TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateAcceptAutofillSuggestion) { EXPECT_CALL(autofill_client_, HideAutofillPopup()); @@ -605,11 +604,9 @@ FillOrPreviewForm( AutofillDriver::FORM_DATA_ACTION_FILL, _, _, _, kAutofillProfileId)); - base::HistogramTester histogram; external_delegate_->DidAcceptSuggestion(dummy_string, kAutofillProfileId, 2); // Row 2 - histogram.ExpectUniqueSample("Autofill.SuggestionAcceptedIndex", 2, 1); } // Test that the driver is directed to clear the form after being notified that
diff --git a/components/autofill/core/browser/autofill_metrics.cc b/components/autofill/core/browser/autofill_metrics.cc index a7b9a41..491efbd 100644 --- a/components/autofill/core/browser/autofill_metrics.cc +++ b/components/autofill/core/browser/autofill_metrics.cc
@@ -1459,10 +1459,23 @@ } // static -void AutofillMetrics::LogAutofillSuggestionAcceptedIndex(int index) { +void AutofillMetrics::LogAutofillSuggestionAcceptedIndex(int index, + PopupType popup_type) { base::UmaHistogramSparse("Autofill.SuggestionAcceptedIndex", std::min(index, kMaxBucketsCount)); + if (popup_type == PopupType::kCreditCards) { + base::UmaHistogramSparse("Autofill.SuggestionAcceptedIndex.CreditCard", + std::min(index, kMaxBucketsCount)); + } else if (popup_type == PopupType::kAddresses || + popup_type == PopupType::kPersonalInformation) { + base::UmaHistogramSparse("Autofill.SuggestionAcceptedIndex.Profile", + std::min(index, kMaxBucketsCount)); + } else { + base::UmaHistogramSparse("Autofill.SuggestionAcceptedIndex.Other", + std::min(index, kMaxBucketsCount)); + } + base::RecordAction(base::UserMetricsAction("Autofill_SelectedSuggestion")); }
diff --git a/components/autofill/core/browser/autofill_metrics.h b/components/autofill/core/browser/autofill_metrics.h index 5ffab78..425c595 100644 --- a/components/autofill/core/browser/autofill_metrics.h +++ b/components/autofill/core/browser/autofill_metrics.h
@@ -20,6 +20,7 @@ #include "components/autofill/core/browser/form_types.h" #include "components/autofill/core/browser/metrics/form_events.h" #include "components/autofill/core/browser/sync_utils.h" +#include "components/autofill/core/browser/ui/popup_types.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/signatures_util.h" #include "components/security_state/core/security_state.h" @@ -1159,7 +1160,8 @@ static void LogAddressSuggestionsCount(size_t num_suggestions); // Log the index of the selected Autofill suggestion in the popup. - static void LogAutofillSuggestionAcceptedIndex(int index); + static void LogAutofillSuggestionAcceptedIndex(int index, + PopupType popup_type); // Logs that the user cleared the form. static void LogAutofillFormCleared();
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc index 7f7c272..584b79b 100644 --- a/components/autofill/core/browser/autofill_metrics_unittest.cc +++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -38,6 +38,7 @@ #include "components/autofill/core/browser/test_form_structure.h" #include "components/autofill/core/browser/test_personal_data_manager.h" #include "components/autofill/core/browser/ui/popup_item_ids.h" +#include "components/autofill/core/browser/ui/popup_types.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_features.h" @@ -63,6 +64,7 @@ using base::Bucket; using base::TimeTicks; using ::testing::ElementsAre; +using ::testing::HasSubstr; using ::testing::Matcher; using ::testing::UnorderedPointwise; @@ -8606,6 +8608,59 @@ /*expected_count=*/1); } +TEST_F(AutofillMetricsTest, LogSuggestionAcceptedIndex_CreditCard) { + const int index = 2; + const PopupType popup_type = PopupType::kCreditCards; + + base::HistogramTester histogram_tester; + AutofillMetrics::LogAutofillSuggestionAcceptedIndex(index, popup_type); + histogram_tester.ExpectUniqueSample( + "Autofill.SuggestionAcceptedIndex.CreditCard", index, 1); + + const std::string histograms = histogram_tester.GetAllHistogramsRecorded(); + EXPECT_THAT( + histograms, + Not(AnyOf(HasSubstr("Autofill.SuggestionAcceptedIndex.Other"), + HasSubstr("Autofill.SuggestionAcceptedIndex.Profile")))); +} + +TEST_F(AutofillMetricsTest, LogSuggestionAcceptedIndex_Profile) { + const int index = 1; + const PopupType popup_type1 = PopupType::kPersonalInformation; + const PopupType popup_type2 = PopupType::kAddresses; + + base::HistogramTester histogram_tester; + AutofillMetrics::LogAutofillSuggestionAcceptedIndex(index, popup_type1); + AutofillMetrics::LogAutofillSuggestionAcceptedIndex(index, popup_type2); + histogram_tester.ExpectUniqueSample( + "Autofill.SuggestionAcceptedIndex.Profile", index, 2); + + const std::string histograms = histogram_tester.GetAllHistogramsRecorded(); + EXPECT_THAT( + histograms, + Not(AnyOf(HasSubstr("Autofill.SuggestionAcceptedIndex.CreditCard"), + HasSubstr("Autofill.SuggestionAcceptedIndex.Other")))); +} + +TEST_F(AutofillMetricsTest, LogSuggestionAcceptedIndex_Other) { + const int index = 0; + const PopupType popup_type1 = PopupType::kUnspecified; + const PopupType popup_type2 = PopupType::kPasswords; + + base::HistogramTester histogram_tester; + AutofillMetrics::LogAutofillSuggestionAcceptedIndex(index, popup_type1); + AutofillMetrics::LogAutofillSuggestionAcceptedIndex(index, popup_type2); + + histogram_tester.ExpectUniqueSample("Autofill.SuggestionAcceptedIndex.Other", + index, 2); + + const std::string histograms = histogram_tester.GetAllHistogramsRecorded(); + EXPECT_THAT( + histograms, + Not(AnyOf(HasSubstr("Autofill.SuggestionAcceptedIndex.CreditCard"), + HasSubstr("Autofill.SuggestionAcceptedIndex.Profile")))); +} + TEST_F(AutofillMetricsTest, OnAutocompleteSuggestionsShown) { base::HistogramTester histogram_tester; AutofillMetrics::OnAutocompleteSuggestionsShown();
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc index 32526960..77043ee4 100644 --- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc +++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
@@ -11,7 +11,6 @@ #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h" -#include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h" #include "components/data_reduction_proxy/core/common/uma_util.h" #include "net/base/load_flags.h" @@ -49,14 +48,17 @@ DataReductionProxyURLLoaderThrottle::~DataReductionProxyURLLoaderThrottle() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!private_data_reduction_proxy_info_ && !private_data_reduction_proxy_) + if (manager_) manager_->RemoveSameSequenceObserver(this); } void DataReductionProxyURLLoaderThrottle::DetachFromCurrentSequence() { DETACH_FROM_SEQUENCE(sequence_checker_); - manager_->RemoveSameSequenceObserver(this); + if (manager_) { + manager_->RemoveSameSequenceObserver(this); + manager_ = nullptr; + } data_reduction_proxy_->Clone( mojo::MakeRequest(&private_data_reduction_proxy_info_)); @@ -261,6 +263,14 @@ } } +void DataReductionProxyURLLoaderThrottle::OnThrottleManagerDestroyed( + DataReductionProxyThrottleManager* manager) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(manager, manager_); + manager_->RemoveSameSequenceObserver(this); + manager_ = nullptr; +} + base::Optional<DataReductionProxyTypeInfo> DataReductionProxyURLLoaderThrottle::FindConfiguredDataReductionProxy( const net::ProxyServer& proxy_server) const {
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h index 4e83f3fe..fedf821 100644 --- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h +++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h
@@ -8,14 +8,13 @@ #include <vector> #include "base/sequence_checker.h" -#include "components/data_reduction_proxy/core/common/data_reduction_proxy.mojom.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_server.h" +#include "components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h" #include "content/public/common/url_loader_throttle.h" #include "mojo/public/cpp/bindings/binding.h" namespace data_reduction_proxy { -class DataReductionProxyThrottleManager; struct DataReductionProxyTypeInfo; // Handles Data Reduction Proxy logic that needs to be applied to each request. @@ -27,7 +26,7 @@ // * Marking data reduction proxies to be bypassed for future requests. class DataReductionProxyURLLoaderThrottle : public content::URLLoaderThrottle, - public mojom::DataReductionProxyThrottleConfigObserver { + public DataReductionProxyThrottleConfigCheckedObserver { public: // |manager| is shared between all the DRP Throttles. DataReductionProxyURLLoaderThrottle( @@ -55,9 +54,11 @@ void WillOnCompleteWithError(const network::URLLoaderCompletionStatus& status, bool* defer) override; - // mojom::DataReductionProxyThrottleConfigObserver: + // DataReductionProxyThrottleConfigCheckedObserver: void OnThrottleConfigChanged( mojom::DataReductionProxyThrottleConfigPtr config) override; + void OnThrottleManagerDestroyed( + DataReductionProxyThrottleManager* manager) override; private: // As the throttle instance is being moved to another sequence, this @@ -90,6 +91,9 @@ std::vector<GURL> url_chain_; std::string request_method_; + // The throttle must be initialized with a valid manager, but can later be + // disassociated from it if the manager is destroyed earlier or if the + // throttle is moved to a different sequence. DataReductionProxyThrottleManager* manager_ = nullptr; // Throttles that run on the same sequence as the manager share the manager's
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc index d44e544..9e33e7d 100644 --- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc +++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle_unittest.cc
@@ -5,6 +5,7 @@ #include "components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h" #include "base/run_loop.h" +#include "base/task/post_task.h" #include "base/task/thread_pool/thread_pool.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_task_environment.h" @@ -141,6 +142,31 @@ MockMojoDataReductionProxy mock_mojo_data_reduction_proxy_; }; +TEST_F(DataReductionProxyURLLoaderThrottleTest, ThrottleDiesFirst) { + auto manager = CreateManager(mock_mojo_data_reduction_proxy()); + DataReductionProxyURLLoaderThrottle throttle((net::HttpRequestHeaders()), + manager.get()); +} + +TEST_F(DataReductionProxyURLLoaderThrottleTest, + ThrottleDiesOnDifferentSequence) { + auto manager = CreateManager(mock_mojo_data_reduction_proxy()); + auto throttle = std::make_unique<DataReductionProxyURLLoaderThrottle>( + (net::HttpRequestHeaders()), manager.get()); + throttle->DetachFromCurrentSequence(); + + auto task_runner = + base::CreateSequencedTaskRunnerWithTraits(base::TaskTraits()); + task_runner->DeleteSoon(FROM_HERE, throttle.release()); +} + +TEST_F(DataReductionProxyURLLoaderThrottleTest, ManagerDiesFirst) { + auto manager = CreateManager(mock_mojo_data_reduction_proxy()); + DataReductionProxyURLLoaderThrottle throttle((net::HttpRequestHeaders()), + manager.get()); + manager.reset(); +} + TEST_F(DataReductionProxyURLLoaderThrottleTest, AcceptTransformHeaderSet) { auto manager = CreateManager(mock_mojo_data_reduction_proxy()); DataReductionProxyURLLoaderThrottle throttle(net::HttpRequestHeaders(),
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.cc index 70d8c93..603ab62b1 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.cc
@@ -30,6 +30,11 @@ DataReductionProxyThrottleManager::~DataReductionProxyThrottleManager() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + for (DataReductionProxyThrottleConfigCheckedObserver& observer : + same_sequence_observers_) { + observer.OnThrottleManagerDestroyed(this); + } } void DataReductionProxyThrottleManager::OnThrottleConfigChanged( @@ -37,20 +42,20 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); last_proxy_config_ = config.Clone(); - for (mojom::DataReductionProxyThrottleConfigObserver& observer : + for (DataReductionProxyThrottleConfigCheckedObserver& observer : same_sequence_observers_) { observer.OnThrottleConfigChanged(config.Clone()); } } void DataReductionProxyThrottleManager::AddSameSequenceObserver( - mojom::DataReductionProxyThrottleConfigObserver* observer) { + DataReductionProxyThrottleConfigCheckedObserver* observer) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); same_sequence_observers_.AddObserver(observer); } void DataReductionProxyThrottleManager::RemoveSameSequenceObserver( - mojom::DataReductionProxyThrottleConfigObserver* observer) { + DataReductionProxyThrottleConfigCheckedObserver* observer) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); same_sequence_observers_.RemoveObserver(observer); }
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h index 54cabcfd..62a5a354 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_throttle_manager.h
@@ -13,6 +13,17 @@ namespace data_reduction_proxy { class DataReductionProxyServer; +class DataReductionProxyThrottleManager; + +// A throttle config observer that is additionally notified about the manager's +// destruction. +class DataReductionProxyThrottleConfigCheckedObserver + : public mojom::DataReductionProxyThrottleConfigObserver, + public base::CheckedObserver { + public: + virtual void OnThrottleManagerDestroyed( + DataReductionProxyThrottleManager* manager) = 0; +}; // Helper that encapsulates the shared state between // DataReductionProxyURLThrottles, whose main responsibility is keeping the @@ -36,9 +47,9 @@ // sign up for / sign out of receiving // mojom::DataReductionProxyThrottleConfigObserver events. void AddSameSequenceObserver( - mojom::DataReductionProxyThrottleConfigObserver* observer); + DataReductionProxyThrottleConfigCheckedObserver* observer); void RemoveSameSequenceObserver( - mojom::DataReductionProxyThrottleConfigObserver* observer); + DataReductionProxyThrottleConfigCheckedObserver* observer); mojom::DataReductionProxy* data_reduction_proxy() { return shared_data_reduction_proxy_; @@ -57,7 +68,8 @@ // for the interfaces mojom::DataReductionProxy and // mojom::DataReductionProxyThrottleConfigObserver. mojom::DataReductionProxy* const shared_data_reduction_proxy_; - base::ObserverList<mojom::DataReductionProxyThrottleConfigObserver>::Unchecked + base::ObserverList<DataReductionProxyThrottleConfigCheckedObserver, + /* check_empty = */ true> same_sequence_observers_; mojo::Binding<
diff --git a/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc b/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc index c984483..2ca5e8f4 100644 --- a/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc +++ b/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
@@ -230,7 +230,7 @@ DomDistillerViewerSource::~DomDistillerViewerSource() {} -std::string DomDistillerViewerSource::GetSource() const { +std::string DomDistillerViewerSource::GetSource() { return scheme_ + "://"; } @@ -292,8 +292,7 @@ callback.Run(base::RefCountedString::TakeString(&unsafe_page_html)); } -std::string DomDistillerViewerSource::GetMimeType( - const std::string& path) const { +std::string DomDistillerViewerSource::GetMimeType(const std::string& path) { if (kViewerCssPath == path) return "text/css"; if (kViewerLoadingImagePath == path) @@ -304,15 +303,15 @@ bool DomDistillerViewerSource::ShouldServiceRequest( const GURL& url, content::ResourceContext* resource_context, - int render_process_id) const { + int render_process_id) { return url.SchemeIs(scheme_); } -std::string DomDistillerViewerSource::GetContentSecurityPolicyStyleSrc() const { +std::string DomDistillerViewerSource::GetContentSecurityPolicyStyleSrc() { return "style-src 'self' https://fonts.googleapis.com;"; } -std::string DomDistillerViewerSource::GetContentSecurityPolicyChildSrc() const { +std::string DomDistillerViewerSource::GetContentSecurityPolicyChildSrc() { return "child-src *;"; }
diff --git a/components/dom_distiller/content/browser/dom_distiller_viewer_source.h b/components/dom_distiller/content/browser/dom_distiller_viewer_source.h index 6b91b1e..1a285dc5 100644 --- a/components/dom_distiller/content/browser/dom_distiller_viewer_source.h +++ b/components/dom_distiller/content/browser/dom_distiller_viewer_source.h
@@ -29,17 +29,17 @@ class RequestViewerHandle; // Overridden from content::URLDataSource: - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const content::ResourceRequestInfo::WebContentsGetter& wc_getter, const content::URLDataSource::GotDataCallback& callback) override; - std::string GetMimeType(const std::string& path) const override; + std::string GetMimeType(const std::string& path) override; bool ShouldServiceRequest(const GURL& url, content::ResourceContext* resource_context, - int render_process_id) const override; - std::string GetContentSecurityPolicyStyleSrc() const override; - std::string GetContentSecurityPolicyChildSrc() const override; + int render_process_id) override; + std::string GetContentSecurityPolicyStyleSrc() override; + std::string GetContentSecurityPolicyChildSrc() override; private: friend class DomDistillerViewerSourceTest;
diff --git a/components/feature_engagement/OWNERS b/components/feature_engagement/OWNERS index f388905..e80cdac 100644 --- a/components/feature_engagement/OWNERS +++ b/components/feature_engagement/OWNERS
@@ -1,5 +1,12 @@ dtrainor@chromium.org nyquist@chromium.org +per-file *event_constants.*=twellington@chromium.org +per-file *feature_configurations.cc=twellington@chromium.org +per-file *feature_constants.*=twellington@chromium.org +per-file *feature_list.*=twellington@chromium.org +per-file *EventConstants.java=twellington@chromium.org +per-file *FeatureConstants.java=twellington@chromium.org + # COMPONENT: Internals>FeatureEngagement
diff --git a/components/flags_ui/resources/flags.css b/components/flags_ui/resources/flags.css index 589c80a..054ee4b 100644 --- a/components/flags_ui/resources/flags.css +++ b/components/flags_ui/resources/flags.css
@@ -334,6 +334,7 @@ background: var(--input-background); border-radius: 3px; box-sizing: border-box; + color: inherit; font-size: .8125rem; margin: 0; min-height: 3em;
diff --git a/components/ntp_snippets/remote/json_request_unittest.cc b/components/ntp_snippets/remote/json_request_unittest.cc index 863b6450..0f084c1 100644 --- a/components/ntp_snippets/remote/json_request_unittest.cc +++ b/components/ntp_snippets/remote/json_request_unittest.cc
@@ -25,6 +25,13 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +// TODO(crbug.com/961023): Fix memory leaks in tests and re-enable on LSAN. +#ifdef LEAK_SANITIZER +#define MAYBE_BuildRequestAuthenticated DISABLED_BuildRequestAuthenticated +#else +#define MAYBE_BuildRequestAuthenticated BuildRequestAuthenticated +#endif + namespace ntp_snippets { namespace internal { @@ -110,7 +117,7 @@ DISALLOW_COPY_AND_ASSIGN(JsonRequestTest); }; -TEST_F(JsonRequestTest, BuildRequestAuthenticated) { +TEST_F(JsonRequestTest, MAYBE_BuildRequestAuthenticated) { JsonRequest::Builder builder = CreateMinimalBuilder(); RequestParams params; params.excluded_ids = {"1234567890"};
diff --git a/components/ntp_tiles/section_type.h b/components/ntp_tiles/section_type.h index f1f9f5c..f72a149 100644 --- a/components/ntp_tiles/section_type.h +++ b/components/ntp_tiles/section_type.h
@@ -10,7 +10,7 @@ // The type of a section means all its tiles originate here. Ranked descendingly // from most important section to least important. // A Java counterpart will be generated for this enum. -// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions +// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions.tile // GENERATED_JAVA_CLASS_NAME_OVERRIDE: TileSectionType enum class SectionType { UNKNOWN,
diff --git a/components/ntp_tiles/tile_source.h b/components/ntp_tiles/tile_source.h index 899e94c..c06f81d1 100644 --- a/components/ntp_tiles/tile_source.h +++ b/components/ntp_tiles/tile_source.h
@@ -10,7 +10,7 @@ // The source of an NTP tile. Please update webui/ntp-tiles-internals* as well // when modifying these values. // A Java counterpart will be generated for this enum. -// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions +// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions.tile enum class TileSource { // Tile comes from the personal top sites list, based on local history. TOP_SITES,
diff --git a/components/ntp_tiles/tile_title_source.h b/components/ntp_tiles/tile_title_source.h index ed5fdd90..9a2370d 100644 --- a/components/ntp_tiles/tile_title_source.h +++ b/components/ntp_tiles/tile_title_source.h
@@ -13,7 +13,7 @@ // enums.xml AND in chrome/browser/resources/local_ntp/most_visited_single.js. // // A Java counterpart will be generated for this enum. -// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions +// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions.tile enum class TileTitleSource { // The title might be invalid, aggregated, user-set, extracted from history, // not loaded or simply not known.
diff --git a/components/ntp_tiles/tile_visual_type.h b/components/ntp_tiles/tile_visual_type.h index 6c57f21..7466e46 100644 --- a/components/ntp_tiles/tile_visual_type.h +++ b/components/ntp_tiles/tile_visual_type.h
@@ -13,7 +13,7 @@ // histograms/enums.xml. // // A Java counterpart will be generated for this enum. -// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions +// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.suggestions.tile enum TileVisualType { // The icon or thumbnail hasn't loaded yet. NONE = 0,
diff --git a/components/policy/core/common/policy_map.cc b/components/policy/core/common/policy_map.cc index 119c0c7..7698e3e 100644 --- a/components/policy/core/common/policy_map.cc +++ b/components/policy/core/common/policy_map.cc
@@ -151,8 +151,7 @@ bool PolicyMap::Entry::IsBlockedOrIgnored() const { return error_message_ids_.find(IDS_POLICY_BLOCKED) != error_message_ids_.end() || - error_message_ids_.find(IDS_POLICY_IGNORED_BY_GROUP_MERGING) != - error_message_ids_.end(); + IsIgnoredByAtomicGroup(); } void PolicyMap::Entry::SetBlocked() { @@ -163,6 +162,11 @@ error_message_ids_.insert(IDS_POLICY_IGNORED_BY_GROUP_MERGING); } +bool PolicyMap::Entry::IsIgnoredByAtomicGroup() const { + return error_message_ids_.find(IDS_POLICY_IGNORED_BY_GROUP_MERGING) != + error_message_ids_.end(); +} + PolicyMap::PolicyMap() {} PolicyMap::~PolicyMap() { @@ -232,6 +236,11 @@ map_[policy].AddError(message_id); } +bool PolicyMap::IsPolicyIgnoredByAtomicGroup(const std::string& policy) const { + const auto& entry = map_.find(policy); + return entry != map_.end() && entry->second.IsIgnoredByAtomicGroup(); +} + void PolicyMap::SetSourceForAll(PolicySource source) { for (auto& it : map_) { it.second.source = source;
diff --git a/components/policy/core/common/policy_map.h b/components/policy/core/common/policy_map.h index 69195c5..9d01d45d 100644 --- a/components/policy/core/common/policy_map.h +++ b/components/policy/core/common/policy_map.h
@@ -88,6 +88,7 @@ // Marks the policy as ignored because it does not share the priority of // its policy atomic group. void SetIgnoredByPolicyAtomicGroup(); + bool IsIgnoredByAtomicGroup() const; // Callback used to look up a localized string given its l10n message ID. It // should return a UTF-16 string. @@ -146,6 +147,11 @@ // should only be called for policies that are already stored in the map. void AddError(const std::string& policy, int message_id); + // Return True if the policy is set but its value is ignored because it does + // not share the highest priority from its atomic group. Returns False if the + // policy is active or not set. + bool IsPolicyIgnoredByAtomicGroup(const std::string& policy) const; + // For all policies, overwrite the PolicySource with |source|. void SetSourceForAll(PolicySource source);
diff --git a/components/policy/core/common/policy_statistics_collector.cc b/components/policy/core/common/policy_statistics_collector.cc index de3cd16..4c7ce6f 100644 --- a/components/policy/core/common/policy_statistics_collector.cc +++ b/components/policy/core/common/policy_statistics_collector.cc
@@ -62,6 +62,10 @@ base::UmaHistogramSparse("Enterprise.Policies", id); } +void PolicyStatisticsCollector::RecordPolicyIgnoredByAtomicGroup(int id) { + base::UmaHistogramSparse("Enterprise.Policies.IgnoredByPolicyGroup", id); +} + void PolicyStatisticsCollector::CollectStatistics() { const PolicyMap& policies = policy_service_->GetPolicies( PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())); @@ -76,6 +80,13 @@ else NOTREACHED(); } + if (policies.IsPolicyIgnoredByAtomicGroup(it.key())) { + const PolicyDetails* details = get_details_.Run(it.key()); + if (details) + RecordPolicyIgnoredByAtomicGroup(details->id); + else + NOTREACHED(); + } } // Take care of next update.
diff --git a/components/policy/core/common/policy_statistics_collector.h b/components/policy/core/common/policy_statistics_collector.h index f373fd5..2328185 100644 --- a/components/policy/core/common/policy_statistics_collector.h +++ b/components/policy/core/common/policy_statistics_collector.h
@@ -48,6 +48,9 @@ // protected virtual for mocking. virtual void RecordPolicyUse(int id); + // protected virtual for mocking. + virtual void RecordPolicyIgnoredByAtomicGroup(int id); + private: void CollectStatistics(); void ScheduleUpdate(base::TimeDelta delay);
diff --git a/components/policy/core/common/policy_statistics_collector_unittest.cc b/components/policy/core/common/policy_statistics_collector_unittest.cc index 80d24980f8..9ab98f3 100644 --- a/components/policy/core/common/policy_statistics_collector_unittest.cc +++ b/components/policy/core/common/policy_statistics_collector_unittest.cc
@@ -66,6 +66,7 @@ task_runner) {} MOCK_METHOD1(RecordPolicyUse, void(int)); + MOCK_METHOD1(RecordPolicyIgnoredByAtomicGroup, void(int)); }; } // namespace @@ -112,6 +113,12 @@ nullptr); } + void SetPolicyIgnoredByAtomicGroup(const std::string& name) { + SetPolicy(name); + auto* policy = policy_map_.GetMutable(name); + policy->SetIgnoredByPolicyAtomicGroup(); + } + base::TimeDelta GetFirstDelay() const { if (!task_runner_->HasPendingTask()) { ADD_FAILURE(); @@ -186,4 +193,17 @@ EXPECT_EQ(1u, task_runner_->NumPendingTasks()); } +TEST_F(PolicyStatisticsCollectorTest, PolicyIgnoredByAtomicGroup) { + SetPolicyIgnoredByAtomicGroup(kTestPolicy1); + + prefs_.SetInt64(policy_prefs::kLastPolicyStatisticsUpdate, + (base::Time::Now() - update_delay_).ToInternalValue()); + + EXPECT_CALL(*policy_statistics_collector_, + RecordPolicyIgnoredByAtomicGroup(kTestPolicy1Id)); + + policy_statistics_collector_->Initialize(); + EXPECT_EQ(1u, task_runner_->NumPendingTasks()); +} + } // namespace policy
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index 200119e0e..f96fceb 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json
@@ -6266,7 +6266,7 @@ 'caption': '''Screen lock delays''', }, ], - 'supported_on': ['chrome.*:76-'], + 'supported_on': ['chrome.*:76-', 'chrome_os:76-'], 'features': { 'dynamic_refresh': True, 'per_profile': True,
diff --git a/components/previews/content/previews_decider_impl_unittest.cc b/components/previews/content/previews_decider_impl_unittest.cc index e75129c..fdcdf9b 100644 --- a/components/previews/content/previews_decider_impl_unittest.cc +++ b/components/previews/content/previews_decider_impl_unittest.cc
@@ -61,6 +61,19 @@ #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" +// TODO(crbug.com/961023): Fix memory leaks in tests and re-enable on LSAN. +#ifdef LEAK_SANITIZER +#define MAYBE_TestSetBlacklistBoolDueToBlackListState \ + DISABLED_TestSetBlacklistBoolDueToBlackListState +#define MAYBE_TestDisallowPreviewBecauseOfBlackListState \ + DISABLED_TestDisallowPreviewBecauseOfBlackListState +#else +#define MAYBE_TestSetBlacklistBoolDueToBlackListState \ + TestSetBlacklistBoolDueToBlackListState +#define MAYBE_TestDisallowPreviewBecauseOfBlackListState \ + TestDisallowPreviewBecauseOfBlackListState +#endif + namespace previews { namespace { @@ -515,7 +528,8 @@ // Tests most of the reasons that a preview could be disallowed because of the // state of the blacklist. Excluded values are USER_RECENTLY_OPTED_OUT, // USER_BLACKLISTED, HOST_BLACKLISTED. These are internal to the blacklist. -TEST_F(PreviewsDeciderImplTest, TestDisallowPreviewBecauseOfBlackListState) { +TEST_F(PreviewsDeciderImplTest, + MAYBE_TestDisallowPreviewBecauseOfBlackListState) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature(features::kPreviews); base::HistogramTester histogram_tester; @@ -562,7 +576,7 @@ variations::testing::ClearAllVariationParams(); } -TEST_F(PreviewsDeciderImplTest, TestSetBlacklistBoolDueToBlackListState) { +TEST_F(PreviewsDeciderImplTest, MAYBE_TestSetBlacklistBoolDueToBlackListState) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature(features::kPreviews);
diff --git a/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm index 2b89e65..1ffb647 100644 --- a/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm +++ b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm
@@ -33,10 +33,10 @@ if (!_inFullScreen) { auto* window = base::mac::ObjCCast<NativeWidgetMacNSWindow>([self window]); - remote_cocoa::NativeWidgetNSWindowBridge* bridgeImpl = [window bridgeImpl]; - if (bridgeImpl) { - bridgeImpl->host()->GetWindowFrameTitlebarHeight(&overrideTitlebarHeight, - &titlebarHeight); + remote_cocoa::NativeWidgetNSWindowBridge* bridge = [window bridge]; + if (bridge) { + bridge->host()->GetWindowFrameTitlebarHeight(&overrideTitlebarHeight, + &titlebarHeight); } } if (overrideTitlebarHeight) @@ -91,9 +91,9 @@ // Keyboard -> Shortcuts -> Keyboard. Usually Ctrl+F5. The argument (|unknown|) // tends to just be nil. - (void)_handleFocusToolbarHotKey:(id)unknown { - remote_cocoa::NativeWidgetNSWindowBridge* bridgeImpl = [self bridgeImpl]; - if (bridgeImpl) - bridgeImpl->host()->OnFocusWindowToolbar(); + remote_cocoa::NativeWidgetNSWindowBridge* bridge = [self bridge]; + if (bridge) + bridge->host()->OnFocusWindowToolbar(); } @end
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h index b71a4d9..e03bbc72 100644 --- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h +++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h
@@ -58,8 +58,7 @@ @property(assign, nonatomic) uint64_t bridgedNativeWidgetId; // The NativeWidgetNSWindowBridge that this will use to call back to the host. -@property(assign, nonatomic) - remote_cocoa::NativeWidgetNSWindowBridge* bridgeImpl; +@property(assign, nonatomic) remote_cocoa::NativeWidgetNSWindowBridge* bridge; @end #endif // COMPONENTS_REMOTE_COCOA_APP_SHIM_NATIVE_WIDGET_MAC_NSWINDOW_H_
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm index f74d79b..235b4d50 100644 --- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm +++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
@@ -82,10 +82,10 @@ base::scoped_nsprotocol<id<UserInterfaceItemCommandHandler>> commandHandler_; id<WindowTouchBarDelegate> touchBarDelegate_; // Weak. uint64_t bridgedNativeWidgetId_; - remote_cocoa::NativeWidgetNSWindowBridge* bridgeImpl_; + remote_cocoa::NativeWidgetNSWindowBridge* bridge_; } @synthesize bridgedNativeWidgetId = bridgedNativeWidgetId_; -@synthesize bridgeImpl = bridgeImpl_; +@synthesize bridge = bridge_; - (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle @@ -136,14 +136,14 @@ - (BOOL)hasViewsMenuActive { bool hasMenuController = false; - if (bridgeImpl_) - bridgeImpl_->host()->GetHasMenuController(&hasMenuController); + if (bridge_) + bridge_->host()->GetHasMenuController(&hasMenuController); return hasMenuController; } - (id<NSAccessibility>)rootAccessibilityObject { id<NSAccessibility> obj = - bridgeImpl_ ? bridgeImpl_->host_helper()->GetNativeViewAccessible() : nil; + bridge_ ? bridge_->host_helper()->GetNativeViewAccessible() : nil; // We should like to DCHECK that the object returned implemements the // NSAccessibility protocol, but the NSAccessibilityRemoteUIElement interface // does not conform. @@ -166,8 +166,8 @@ - (BOOL)_isTitleHidden { bool shouldShowWindowTitle = YES; - if (bridgeImpl_) - bridgeImpl_->host()->GetShouldShowWindowTitle(&shouldShowWindowTitle); + if (bridge_) + bridge_->host()->GetShouldShowWindowTitle(&shouldShowWindowTitle); return !shouldShowWindowTitle; } @@ -184,22 +184,22 @@ // down, so check for a delegate. - (BOOL)canBecomeKeyWindow { bool canBecomeKey = NO; - if (bridgeImpl_) - bridgeImpl_->host()->GetCanWindowBecomeKey(&canBecomeKey); + if (bridge_) + bridge_->host()->GetCanWindowBecomeKey(&canBecomeKey); return canBecomeKey; } - (BOOL)canBecomeMainWindow { - if (!bridgeImpl_) + if (!bridge_) return NO; // Dialogs and bubbles shouldn't take large shadows away from their parent. - if (bridgeImpl_->parent()) + if (bridge_->parent()) return NO; bool canBecomeKey = NO; - if (bridgeImpl_) - bridgeImpl_->host()->GetCanWindowBecomeKey(&canBecomeKey); + if (bridge_) + bridge_->host()->GetCanWindowBecomeKey(&canBecomeKey); return canBecomeKey; } @@ -211,9 +211,9 @@ // https://crbug.com/941506. if (![NSThread isMainThread]) return [super hasKeyAppearance]; - if (bridgeImpl_) { + if (bridge_) { bool isAlwaysRenderWindowAsKey = NO; - bridgeImpl_->host()->GetAlwaysRenderWindowAsKey(&isAlwaysRenderWindowAsKey); + bridge_->host()->GetAlwaysRenderWindowAsKey(&isAlwaysRenderWindowAsKey); if (isAlwaysRenderWindowAsKey) return YES; } @@ -341,10 +341,10 @@ // properties on the NSWindow and repeats them when focusing an item in the // RootView's a11y group. See http://crbug.com/748221. id superFocus = [super accessibilityFocusedUIElement]; - if (!bridgeImpl_ || superFocus != self) + if (!bridge_ || superFocus != self) return superFocus; - return bridgeImpl_->host_helper()->GetNativeViewAccessible(); + return bridge_->host_helper()->GetNativeViewAccessible(); } - (NSString*)accessibilityTitle {
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm index 0a12453..af9e024a 100644 --- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm +++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
@@ -336,7 +336,7 @@ window_delegate_.reset( [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); window_ = std::move(window); - [window_ setBridgeImpl:this]; + [window_ setBridge:this]; [window_ setBridgedNativeWidgetId:id_]; [window_ setReleasedWhenClosed:NO]; // Owned by scoped_nsobject. [window_ setDelegate:window_delegate_]; @@ -831,7 +831,7 @@ DCHECK(!show_animation_); [window_ setDelegate:nil]; - [window_ setBridgeImpl:nullptr]; + [window_ setBridge:nullptr]; // Ensure that |this| cannot be reached by its id while it is being destroyed. size_t erased = GetIdToWidgetImplMap().erase(id_);
diff --git a/components/remote_cocoa/browser/window.mm b/components/remote_cocoa/browser/window.mm index 071b741..02fc2caa 100644 --- a/components/remote_cocoa/browser/window.mm +++ b/components/remote_cocoa/browser/window.mm
@@ -14,15 +14,15 @@ } // namespace remote_cocoa @interface NSWindow (Private) -- (remote_cocoa::NativeWidgetNSWindowBridge*)bridgeImpl; +- (remote_cocoa::NativeWidgetNSWindowBridge*)bridge; @end namespace remote_cocoa { bool IsWindowRemote(gfx::NativeWindow gfx_window) { NSWindow* ns_window = gfx_window.GetNativeNSWindow(); - if ([ns_window respondsToSelector:@selector(bridgeImpl)]) { - if (![ns_window bridgeImpl]) + if ([ns_window respondsToSelector:@selector(bridge)]) { + if (![ns_window bridge]) return true; } return false;
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.cc b/components/signin/core/browser/gaia_cookie_manager_service.cc index 1d2074f..4e932a49 100644 --- a/components/signin/core/browser/gaia_cookie_manager_service.cc +++ b/components/signin/core/browser/gaia_cookie_manager_service.cc
@@ -654,6 +654,14 @@ } } +void GaiaCookieManagerService::AddObserver(Observer* observer) { + observer_list_.AddObserver(observer); +} + +void GaiaCookieManagerService::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); +} + void GaiaCookieManagerService::CancelAll() { VLOG(1) << "GaiaCookieManagerService::CancelAll"; gaia_auth_fetcher_.reset(); @@ -687,8 +695,8 @@ if (cause == network::mojom::CookieChangeCause::EXPLICIT) { DCHECK(net::CookieChangeCauseIsDeletion(net::CookieChangeCause::EXPLICIT)); - if (gaia_cookie_deleted_by_user_action_callback_) { - gaia_cookie_deleted_by_user_action_callback_.Run(); + for (auto& observer : observer_list_) { + observer.OnGaiaCookieDeletedByUserAction(); } } @@ -736,18 +744,6 @@ requests_.front().RunSetAccountsInCookieCompletedCallback(result); } -void GaiaCookieManagerService::SetGaiaAccountsInCookieUpdatedCallback( - GaiaAccountsInCookieUpdatedCallback callback) { - DCHECK(!gaia_accounts_updated_in_cookie_callback_); - gaia_accounts_updated_in_cookie_callback_ = std::move(callback); -} - -void GaiaCookieManagerService::SetGaiaCookieDeletedByUserActionCallback( - GaiaCookieDeletedByUserActionCallback callback) { - DCHECK(!gaia_cookie_deleted_by_user_action_callback_); - gaia_cookie_deleted_by_user_action_callback_ = std::move(callback); -} - void GaiaCookieManagerService::OnUbertokenFetchComplete( GoogleServiceAuthError error, const std::string& uber_token) { @@ -856,8 +852,8 @@ // services, in response to OnGaiaAccountsInCookieUpdated, may try in return // to call ListAccounts, which would immediately return false if the // ListAccounts request is still sitting in queue. - if (gaia_accounts_updated_in_cookie_callback_) { - gaia_accounts_updated_in_cookie_callback_.Run( + for (auto& observer : observer_list_) { + observer.OnGaiaAccountsInCookieUpdated( listed_accounts_, signed_out_accounts_, GoogleServiceAuthError(GoogleServiceAuthError::NONE)); } @@ -885,12 +881,10 @@ } RecordListAccountsFailure(error.state()); - - if (gaia_accounts_updated_in_cookie_callback_) { - gaia_accounts_updated_in_cookie_callback_.Run(listed_accounts_, - signed_out_accounts_, error); + for (auto& observer : observer_list_) { + observer.OnGaiaAccountsInCookieUpdated(listed_accounts_, + signed_out_accounts_, error); } - HandleNextRequest(); }
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.h b/components/signin/core/browser/gaia_cookie_manager_service.h index 77055d1c..f3fbd22 100644 --- a/components/signin/core/browser/gaia_cookie_manager_service.h +++ b/components/signin/core/browser/gaia_cookie_manager_service.h
@@ -88,12 +88,6 @@ const GoogleServiceAuthError&)> AddAccountToCookieCompletedCallback; - typedef base::RepeatingCallback<void(const std::vector<gaia::ListedAccount>&, - const std::vector<gaia::ListedAccount>&, - const GoogleServiceAuthError&)> - GaiaAccountsInCookieUpdatedCallback; - typedef base::RepeatingCallback<void()> GaiaCookieDeletedByUserActionCallback; - // Contains the information and parameters for any request. class GaiaCookieRequest { public: @@ -154,6 +148,28 @@ DISALLOW_COPY_AND_ASSIGN(GaiaCookieRequest); }; + class Observer { + public: + // Called whenever the GaiaCookieManagerService's list of GAIA accounts is + // updated. The GCMS monitors the APISID cookie and triggers a /ListAccounts + // call on change. The GCMS will also call ListAccounts upon the first call + // to ListAccounts(). The GCMS will delay calling ListAccounts if other + // requests are in queue that would modify the APISID cookie. + // If the ListAccounts call fails and the GCMS cannot recover, the reason + // is passed in |error|. + virtual void OnGaiaAccountsInCookieUpdated( + const std::vector<gaia::ListedAccount>& accounts, + const std::vector<gaia::ListedAccount>& signed_out_accounts, + const GoogleServiceAuthError& error) {} + + // Called when the Gaia cookie has been deleted explicitly by a user action, + // e.g. from the settings or by an extension. + virtual void OnGaiaCookieDeletedByUserAction() {} + + protected: + virtual ~Observer() {} + }; + // Class to retrieve the external connection check results from gaia. // Declared publicly for unit tests. class ExternalCcResultFetcher : public GaiaAuthConsumer { @@ -258,6 +274,10 @@ // service. Virtual for testing. virtual void ForceOnCookieChangeProcessing(); + // Add or remove observers of this helper. + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + // Cancel all login requests. void CancelAll(); @@ -279,25 +299,6 @@ list_accounts_stale_ = stale; } - // If set, this callback will be invoked whenever the - // GaiaCookieManagerService's list of GAIA accounts is updated. The GCMS - // monitors the APISID cookie and triggers a /ListAccounts call on change. - // The GCMS will also call ListAccounts upon the first call to - // ListAccounts(). The GCMS will delay calling ListAccounts if other - // requests are in queue that would modify the APISID cookie. - // If the ListAccounts call fails and the GCMS cannot recover, the reason - // is passed in |error|. - // This method can only be called once. - void SetGaiaAccountsInCookieUpdatedCallback( - GaiaAccountsInCookieUpdatedCallback callback); - - // If set, this callback will be invoked whenever the Gaia cookie has - // been deleted explicitly by a user action, e.g. from the settings or by an - // extension. - // This method can only be called once. - void SetGaiaCookieDeletedByUserActionCallback( - GaiaCookieDeletedByUserActionCallback callback); - // Returns a non-NULL pointer to its instance of net::BackoffEntry const net::BackoffEntry* GetBackoffEntry() { return &fetcher_backoff_; } @@ -364,11 +365,6 @@ OAuth2TokenService* token_service_; SigninClient* signin_client_; - GaiaAccountsInCookieUpdatedCallback gaia_accounts_updated_in_cookie_callback_; - GaiaCookieDeletedByUserActionCallback - gaia_cookie_deleted_by_user_action_callback_; - base::RepeatingCallback<scoped_refptr<network::SharedURLLoaderFactory>()> - shared_url_loader_factory_getter_; std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_; std::unique_ptr<signin::UbertokenFetcherImpl> uber_token_fetcher_; ExternalCcResultFetcher external_cc_result_fetcher_; @@ -394,6 +390,10 @@ // executed at a time. base::circular_deque<GaiaCookieRequest> requests_; + // List of observers to notify when merge session completes. + // Makes sure list is empty on destruction. + base::ObserverList<Observer, true>::Unchecked observer_list_; + // True once the ExternalCCResultFetcher has completed once. bool external_cc_result_fetched_;
diff --git a/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc b/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc index 5a27c8f..80c2108c 100644 --- a/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc +++ b/components/signin/core/browser/gaia_cookie_manager_service_unittest.cc
@@ -38,19 +38,21 @@ using MockAddAccountToCookieCompletedCallback = base::MockCallback< GaiaCookieManagerService::AddAccountToCookieCompletedCallback>; -class MockObserver { +class MockObserver : public GaiaCookieManagerService::Observer { public: - explicit MockObserver(GaiaCookieManagerService* helper) { - helper->SetGaiaAccountsInCookieUpdatedCallback(base::BindRepeating( - &MockObserver::OnGaiaAccountsInCookieUpdated, base::Unretained(this))); + explicit MockObserver(GaiaCookieManagerService* helper) : helper_(helper) { + helper_->AddObserver(this); } + ~MockObserver() override { helper_->RemoveObserver(this); } + MOCK_METHOD3(OnGaiaAccountsInCookieUpdated, void(const std::vector<gaia::ListedAccount>&, const std::vector<gaia::ListedAccount>&, const GoogleServiceAuthError&)); private: + GaiaCookieManagerService* helper_; DISALLOW_COPY_AND_ASSIGN(MockObserver); };
diff --git a/components/signin/core/browser/identity_manager_wrapper.cc b/components/signin/core/browser/identity_manager_wrapper.cc index fab2d43..2025ebc 100644 --- a/components/signin/core/browser/identity_manager_wrapper.cc +++ b/components/signin/core/browser/identity_manager_wrapper.cc
@@ -5,7 +5,6 @@ #include "components/signin/core/browser/identity_manager_wrapper.h" #include "components/keyed_service/core/keyed_service.h" -#include "components/signin/core/browser/gaia_cookie_manager_service.h" #include "services/identity/public/cpp/accounts_cookie_mutator.h" #include "services/identity/public/cpp/accounts_mutator.h" #include "services/identity/public/cpp/diagnostics_provider.h"
diff --git a/components/sync/driver/sync_session_durations_metrics_recorder.h b/components/sync/driver/sync_session_durations_metrics_recorder.h index b69ed49..f4860817 100644 --- a/components/sync/driver/sync_session_durations_metrics_recorder.h +++ b/components/sync/driver/sync_session_durations_metrics_recorder.h
@@ -20,7 +20,8 @@ // Tracks the active browsing time that the user spends signed in and/or syncing // as fraction of their total browsing time. class SyncSessionDurationsMetricsRecorder - : public syncer::SyncServiceObserver, + : public GaiaCookieManagerService::Observer, + public syncer::SyncServiceObserver, public identity::IdentityManager::Observer { public: // Callers must ensure that the parameters outlive this object.
diff --git a/components/timers/OWNERS b/components/timers/OWNERS index 0c43483..8a33102 100644 --- a/components/timers/OWNERS +++ b/components/timers/OWNERS
@@ -1,2 +1 @@ chirantan@chromium.org -derat@chromium.org
diff --git a/components/ui_devtools/BUILD.gn b/components/ui_devtools/BUILD.gn index a0e8576..1f221f2 100644 --- a/components/ui_devtools/BUILD.gn +++ b/components/ui_devtools/BUILD.gn
@@ -88,11 +88,11 @@ } deps = [ + ":devtools_protocol_encoding", ":protocol_generated_sources", "//base", "//net", "//ui/gfx", - "//third_party/inspector_protocol:encoding", ] public_deps = [ @@ -108,11 +108,23 @@ "ui_devtools_unittest_utils.h", ] public_deps = [ + ":devtools_protocol_encoding", ":ui_devtools", "//testing/gmock", ] } +static_library("devtools_protocol_encoding") { + sources = [ + "devtools_protocol_encoding.cc", + "devtools_protocol_encoding.h", + ] + deps = [ + "//base", + "//third_party/inspector_protocol:encoding", + ] +} + source_set("unit_tests") { testonly = true
diff --git a/components/ui_devtools/DEPS b/components/ui_devtools/DEPS index 73b01ba..9dab257 100644 --- a/components/ui_devtools/DEPS +++ b/components/ui_devtools/DEPS
@@ -4,6 +4,7 @@ "+services/network/public/cpp", "+services/network/public/mojom", "+third_party/blink/renderer/platform/inspector_protocol", + "+third_party/inspector_protocol", "+ui/gfx", ]
diff --git a/components/ui_devtools/devtools_client.cc b/components/ui_devtools/devtools_client.cc index 865f209b1..e84fdace 100644 --- a/components/ui_devtools/devtools_client.cc +++ b/components/ui_devtools/devtools_client.cc
@@ -4,6 +4,7 @@ #include "components/ui_devtools/devtools_client.h" +#include "components/ui_devtools/devtools_protocol_encoding.h" #include "components/ui_devtools/devtools_server.h" namespace ui_devtools { @@ -56,17 +57,32 @@ agent->Disable(); } +namespace { +std::string SerializeToJSON(std::unique_ptr<protocol::Serializable> message) { + std::vector<uint8_t> cbor = message->serializeToBinary(); + std::string json; + ::inspector_protocol_encoding::Status status = + ConvertCBORToJSON(::inspector_protocol_encoding::SpanFrom(cbor), &json); + LOG_IF(ERROR, !status.ok()) << status.ToASCIIString(); + return json; +} +} // namespace + void UiDevToolsClient::sendProtocolResponse( int callId, std::unique_ptr<protocol::Serializable> message) { - if (connected()) - server_->SendOverWebSocket(connection_id_, message->serialize(false)); + if (connected()) { + server_->SendOverWebSocket( + connection_id_, base::StringPiece(SerializeToJSON(std::move(message)))); + } } void UiDevToolsClient::sendProtocolNotification( std::unique_ptr<protocol::Serializable> message) { - if (connected()) - server_->SendOverWebSocket(connection_id_, message->serialize(false)); + if (connected()) { + server_->SendOverWebSocket( + connection_id_, base::StringPiece(SerializeToJSON(std::move(message)))); + } } void UiDevToolsClient::flushProtocolNotifications() {
diff --git a/components/ui_devtools/devtools_protocol_encoding.cc b/components/ui_devtools/devtools_protocol_encoding.cc new file mode 100644 index 0000000..760ca2a --- /dev/null +++ b/components/ui_devtools/devtools_protocol_encoding.cc
@@ -0,0 +1,38 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/ui_devtools/devtools_protocol_encoding.h" + +#include <memory> +#include "base/strings/string_number_conversions.h" + +namespace ui_devtools { +namespace { +using ::inspector_protocol_encoding::span; +using IPEStatus = ::inspector_protocol_encoding::Status; + +// Platform allows us to inject the string<->double conversion +// routines from base:: into the inspector_protocol JSON parser / serializer. +class Platform : public ::inspector_protocol_encoding::json::Platform { + public: + bool StrToD(const char* str, double* result) const override { + return base::StringToDouble(str, result); + } + + // Prints |value| in a format suitable for JSON. + std::unique_ptr<char[]> DToStr(double value) const override { + std::string str = base::NumberToString(value); + std::unique_ptr<char[]> result(new char[str.size() + 1]); + memcpy(result.get(), str.c_str(), str.size() + 1); + return result; + } +}; +} // namespace + +IPEStatus ConvertCBORToJSON(span<uint8_t> cbor, std::string* json) { + Platform platform; + return ::inspector_protocol_encoding::json::ConvertCBORToJSON(platform, cbor, + json); +} +} // namespace ui_devtools
diff --git a/components/ui_devtools/devtools_protocol_encoding.h b/components/ui_devtools/devtools_protocol_encoding.h new file mode 100644 index 0000000..caf57ed9 --- /dev/null +++ b/components/ui_devtools/devtools_protocol_encoding.h
@@ -0,0 +1,21 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_UI_DEVTOOLS_DEVTOOLS_PROTOCOL_ENCODING_H_ +#define COMPONENTS_UI_DEVTOOLS_DEVTOOLS_PROTOCOL_ENCODING_H_ + +#include "third_party/inspector_protocol/encoding/encoding.h" + +// Convenience adaptation of the conversion function +// ::inspector_protocol_encoding::json::ConvertCBORToJSON, +// using an implementation of +// ::inspector_protocol_encoding::json::Platform that +// delegates to base/strings/string_number_conversions.h. +namespace ui_devtools { +::inspector_protocol_encoding::Status ConvertCBORToJSON( + ::inspector_protocol_encoding::span<uint8_t> cbor, + std::string* json); +} // namespace ui_devtools + +#endif // COMPONENTS_UI_DEVTOOLS_DEVTOOLS_PROTOCOL_ENCODING_H_
diff --git a/components/ui_devtools/devtools_server.cc b/components/ui_devtools/devtools_server.cc index 028697c..a5f2cdd 100644 --- a/components/ui_devtools/devtools_server.cc +++ b/components/ui_devtools/devtools_server.cc
@@ -168,7 +168,7 @@ } void UiDevToolsServer::SendOverWebSocket(int connection_id, - const protocol::String& message) { + base::StringPiece message) { DCHECK_CALLED_ON_VALID_SEQUENCE(devtools_server_sequence_); server_->SendOverWebSocket(connection_id, message, tag_); }
diff --git a/components/ui_devtools/devtools_server.h b/components/ui_devtools/devtools_server.h index 60ff011..33f00552 100644 --- a/components/ui_devtools/devtools_server.h +++ b/components/ui_devtools/devtools_server.h
@@ -10,6 +10,7 @@ #include "base/compiler_specific.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" +#include "base/strings/string_piece_forward.h" #include "base/threading/thread.h" #include "components/ui_devtools/DOM.h" #include "components/ui_devtools/Forward.h" @@ -62,7 +63,7 @@ int default_port); void AttachClient(std::unique_ptr<UiDevToolsClient> client); - void SendOverWebSocket(int connection_id, const protocol::String& message); + void SendOverWebSocket(int connection_id, base::StringPiece message); int port() const { return port_; }
diff --git a/components/ui_devtools/ui_devtools_unittest_utils.cc b/components/ui_devtools/ui_devtools_unittest_utils.cc index a2303d5e..f7b80a4 100644 --- a/components/ui_devtools/ui_devtools_unittest_utils.cc +++ b/components/ui_devtools/ui_devtools_unittest_utils.cc
@@ -5,6 +5,7 @@ #include "components/ui_devtools/ui_devtools_unittest_utils.h" #include "base/strings/string_util.h" +#include "components/ui_devtools/devtools_protocol_encoding.h" namespace ui_devtools { @@ -29,10 +30,22 @@ protocol_notification_messages_.end(), message); } +namespace { +std::string SerializeToJSON(std::unique_ptr<protocol::Serializable> message) { + std::vector<uint8_t> cbor = message->serializeToBinary(); + std::string json; + ::inspector_protocol_encoding::Status status = + ConvertCBORToJSON(::inspector_protocol_encoding::SpanFrom(cbor), &json); + DCHECK(status.ok()) << status.ToASCIIString(); + return json; +} +} // namespace + void FakeFrontendChannel::sendProtocolNotification( std::unique_ptr<protocol::Serializable> message) { EXPECT_TRUE(allow_notifications_); - protocol_notification_messages_.push_back(message->serialize(false)); + protocol_notification_messages_.push_back( + SerializeToJSON(std::move(message))); } } // namespace ui_devtools
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc index b57e7ce6..40ca7db 100644 --- a/components/viz/service/display/display_unittest.cc +++ b/components/viz/service/display/display_unittest.cc
@@ -3396,52 +3396,37 @@ support_->SetNeedsBeginFrame(true); // Helper fn to submit a CF. - auto submit_frame = [this](RenderPassList* pass_list) { + auto submit_frame = [this]() { + RenderPassList pass_list; auto pass = RenderPass::Create(); pass->output_rect = gfx::Rect(0, 0, 100, 100); pass->damage_rect = gfx::Rect(10, 10, 1, 1); pass->id = 1u; - pass_list->push_back(std::move(pass)); + pass_list.push_back(std::move(pass)); SubmitCompositorFrame( - pass_list, + &pass_list, id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id()); }; - // BeginFrame should not be throttled when the client has not submitted any - // compositor frames. - base::TimeTicks frame_time = base::TimeTicks::Now(); - EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); - UpdateBeginFrameTime(support_.get(), frame_time); - - // Submit the first frame for the client. Begin-frame should still not be - // throttled since it has not been embedded yet. - RenderPassList pass_list; - submit_frame(&pass_list); + // Submit kUndrawnFrameLimit+1 frames. BeginFrames should be throttled only + // after the last frame. + base::TimeTicks frame_time; + for (uint32_t i = 0; i < CompositorFrameSinkSupport::kUndrawnFrameLimit + 1; + ++i) { + frame_time = base::TimeTicks::Now(); + EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); + UpdateBeginFrameTime(support_.get(), frame_time); + submit_frame(); + // Immediately after submitting frame, because there is presentation + // feedback queued up, ShouldSendBeginFrame should always return true. + EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); + // Clear the presentation feedbacks. + UpdateBeginFrameTime(support_.get(), frame_time); + } frame_time = base::TimeTicks::Now(); - EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); - UpdateBeginFrameTime(support_.get(), frame_time); - - display_->DrawAndSwap(); - frame_time = base::TimeTicks::Now(); - EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); - UpdateBeginFrameTime(support_.get(), frame_time); - - // Submit a second frame. This frame should not be throttled, even after - // presentation-feedbacks, as we allow up to two undrawn frames. - submit_frame(&pass_list); - frame_time = base::TimeTicks::Now(); - EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); - UpdateBeginFrameTime(support_.get(), frame_time); - EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); - - // Submit a third frame. This frame should be throttled after - // presentation-feedbacks, as we throttle at two undrawn frames. - submit_frame(&pass_list); - frame_time = base::TimeTicks::Now(); - EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); - UpdateBeginFrameTime(support_.get(), frame_time); EXPECT_FALSE(ShouldSendBeginFrame(support_.get(), frame_time)); + UpdateBeginFrameTime(support_.get(), frame_time); // Drawing should unthrottle begin-frames. display_->DrawAndSwap(); @@ -3449,17 +3434,24 @@ EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); UpdateBeginFrameTime(support_.get(), frame_time); - // Submit two more frames. Begin-frame should be throttled after the - // begin-frame for presenatation-feedback. - submit_frame(&pass_list); + // Verify that throttling starts again after kUndrawnFrameLimit+1 frames. + for (uint32_t i = 0; i < CompositorFrameSinkSupport::kUndrawnFrameLimit + 1; + ++i) { + // This clears the presentation feedbacks. + UpdateBeginFrameTime(support_.get(), frame_time); + frame_time = base::TimeTicks::Now(); + EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); + UpdateBeginFrameTime(support_.get(), frame_time); + submit_frame(); + // Immediately after submitting frame, because there is presentation + // feedback queued up, ShouldSendBeginFrame should always return true. + EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); + // Clear the presentation feedbacks. + UpdateBeginFrameTime(support_.get(), frame_time); + } frame_time = base::TimeTicks::Now(); - EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); - UpdateBeginFrameTime(support_.get(), frame_time); - submit_frame(&pass_list); - frame_time = base::TimeTicks::Now(); - EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); - UpdateBeginFrameTime(support_.get(), frame_time); EXPECT_FALSE(ShouldSendBeginFrame(support_.get(), frame_time)); + UpdateBeginFrameTime(support_.get(), frame_time); // Instead of doing a draw, forward time by ~1 seconds. That should unthrottle // the begin-frame. @@ -3469,6 +3461,169 @@ TearDownDisplay(); } +TEST_F(DisplayTest, BeginFrameThrottlingMultipleSurfaces) { + id_allocator_.GenerateId(); + SetUpGpuDisplay(RendererSettings()); + + StubDisplayClient client; + display_->Initialize(&client, manager_.surface_manager()); + display_->SetLocalSurfaceId( + id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id(), + 1.f); + support_->SetNeedsBeginFrame(true); + + // Helper fn to submit a CF. + auto submit_frame = [this]() { + RenderPassList pass_list; + auto pass = RenderPass::Create(); + pass->output_rect = gfx::Rect(0, 0, 100, 100); + pass->damage_rect = gfx::Rect(10, 10, 1, 1); + pass->id = 1u; + pass_list.push_back(std::move(pass)); + + SubmitCompositorFrame( + &pass_list, + id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id()); + }; + + // Submit kUndrawnFrameLimit frames. BeginFrames should be throttled only + // after the last frame. + base::TimeTicks frame_time; + for (uint32_t i = 0; i < CompositorFrameSinkSupport::kUndrawnFrameLimit + 1; + ++i) { + frame_time = base::TimeTicks::Now(); + EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); + UpdateBeginFrameTime(support_.get(), frame_time); + submit_frame(); + // Generate a new LocalSurfaceId for the next submission. + id_allocator_.GenerateId(); + } + frame_time = base::TimeTicks::Now(); + EXPECT_FALSE(ShouldSendBeginFrame(support_.get(), frame_time)); + UpdateBeginFrameTime(support_.get(), frame_time); + + // This only draws the first surface, so we should only be able to send one + // more BeginFrame. + display_->DrawAndSwap(); + frame_time = base::TimeTicks::Now(); + EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); + UpdateBeginFrameTime(support_.get(), frame_time); + + // After this frame submission, we are throttled again. + submit_frame(); + frame_time = base::TimeTicks::Now(); + EXPECT_FALSE(ShouldSendBeginFrame(support_.get(), frame_time)); + UpdateBeginFrameTime(support_.get(), frame_time); + + // Now the last surface is drawn. This should unblock us to submit + // kUndrawnFrameLimit+1 frames again. + display_->SetLocalSurfaceId( + id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id(), + 1.f); + display_->DrawAndSwap(); + id_allocator_.GenerateId(); + for (uint32_t i = 0; i < CompositorFrameSinkSupport::kUndrawnFrameLimit + 1; + ++i) { + frame_time = base::TimeTicks::Now(); + EXPECT_TRUE(ShouldSendBeginFrame(support_.get(), frame_time)); + UpdateBeginFrameTime(support_.get(), frame_time); + submit_frame(); + // Generate a new LocalSurfaceId for the next submission. + id_allocator_.GenerateId(); + } + frame_time = base::TimeTicks::Now(); + EXPECT_FALSE(ShouldSendBeginFrame(support_.get(), frame_time)); + UpdateBeginFrameTime(support_.get(), frame_time); + + TearDownDisplay(); +} + +TEST_F(DisplayTest, DontThrottleWhenParentBlocked) { + id_allocator_.GenerateId(); + SetUpGpuDisplay(RendererSettings()); + + StubDisplayClient client; + display_->Initialize(&client, manager_.surface_manager()); + display_->SetLocalSurfaceId( + id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id(), + 1.f); + support_->SetNeedsBeginFrame(true); + + // Create frame sink for a sub surface. + const LocalSurfaceId sub_local_surface_id(6, + base::UnguessableToken::Create()); + const LocalSurfaceId sub_local_surface_id2(7, + base::UnguessableToken::Create()); + const SurfaceId sub_surface_id2(kAnotherFrameSinkId, sub_local_surface_id2); + + MockCompositorFrameSinkClient sub_client; + + auto sub_support = std::make_unique<CompositorFrameSinkSupport>( + &sub_client, &manager_, kAnotherFrameSinkId, false /* is_root */, + true /* needs_sync_points */); + sub_support->SetNeedsBeginFrame(true); + + // Submit kUndrawnFrameLimit+1 frames. BeginFrames should be throttled only + // after the last frame. + base::TimeTicks frame_time; + for (uint32_t i = 0; i < CompositorFrameSinkSupport::kUndrawnFrameLimit + 1; + ++i) { + frame_time = base::TimeTicks::Now(); + EXPECT_TRUE(ShouldSendBeginFrame(sub_support.get(), frame_time)); + UpdateBeginFrameTime(sub_support.get(), frame_time); + sub_support->SubmitCompositorFrame(sub_local_surface_id, + MakeDefaultCompositorFrame()); + // Immediately after submitting frame, because there is presentation + // feedback queued up, ShouldSendBeginFrame should always return true. + EXPECT_TRUE(ShouldSendBeginFrame(sub_support.get(), frame_time)); + // Clear the presentation feedbacks. + UpdateBeginFrameTime(sub_support.get(), frame_time); + } + frame_time = base::TimeTicks::Now(); + EXPECT_FALSE(ShouldSendBeginFrame(sub_support.get(), frame_time)); + UpdateBeginFrameTime(sub_support.get(), frame_time); + + // Make the display block on |sub_local_surface_id2|. + CompositorFrame frame = + CompositorFrameBuilder() + .AddDefaultRenderPass() + .SetActivationDependencies({sub_surface_id2}) + .SetDeadline(FrameDeadline(base::TimeTicks::Now(), + std::numeric_limits<uint32_t>::max(), + base::TimeDelta::FromSeconds(1), false)) + .Build(); + support_->SubmitCompositorFrame( + id_allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id(), + std::move(frame)); + + for (uint32_t i = 0; i < CompositorFrameSinkSupport::kUndrawnFrameLimit * 3; + ++i) { + frame_time = base::TimeTicks::Now(); + EXPECT_TRUE(ShouldSendBeginFrame(sub_support.get(), frame_time)); + UpdateBeginFrameTime(sub_support.get(), frame_time); + sub_support->SubmitCompositorFrame(sub_local_surface_id, + MakeDefaultCompositorFrame()); + // Immediately after submitting frame, because there is presentation + // feedback queued up, ShouldSendBeginFrame should always return true. + EXPECT_TRUE(ShouldSendBeginFrame(sub_support.get(), frame_time)); + // Clear the presentation feedbacks. + UpdateBeginFrameTime(sub_support.get(), frame_time); + } + + // Now submit to |sub_local_surface_id2|. This should unblock the parent and + // throttling will resume. + frame_time = base::TimeTicks::Now(); + EXPECT_TRUE(ShouldSendBeginFrame(sub_support.get(), frame_time)); + UpdateBeginFrameTime(sub_support.get(), frame_time); + sub_support->SubmitCompositorFrame(sub_local_surface_id2, + MakeDefaultCompositorFrame()); + frame_time = base::TimeTicks::Now(); + EXPECT_FALSE(ShouldSendBeginFrame(sub_support.get(), frame_time)); + UpdateBeginFrameTime(sub_support.get(), frame_time); + + TearDownDisplay(); +} + TEST_F(DisplayTest, InvalidPresentationTimestamps) { RendererSettings settings; id_allocator_.GenerateId();
diff --git a/components/viz/service/display/frame_rate_decider_unittest.cc b/components/viz/service/display/frame_rate_decider_unittest.cc index d2502fbd..d80c066 100644 --- a/components/viz/service/display/frame_rate_decider_unittest.cc +++ b/components/viz/service/display/frame_rate_decider_unittest.cc
@@ -65,7 +65,7 @@ SurfaceInfo surface_info(surface_id, frame_.device_scale_factor(), frame_.size_in_pixels()); auto* surface = - surface_manager_->CreateSurface(surface_client(), surface_info, false); + surface_manager_->CreateSurface(surface_client(), surface_info); { FrameRateDecider::ScopedAggregate scope(frame_rate_decider_.get());
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc index f1ae287..324e69a9 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -153,6 +153,8 @@ } void CompositorFrameSinkSupport::OnSurfaceDrawn(Surface* surface) { + if (last_drawn_frame_index_ >= surface->GetActiveFrameIndex()) + return; last_drawn_frame_index_ = surface->GetActiveFrameIndex(); } @@ -457,8 +459,6 @@ // to determine the freshness of a surface at aggregation time. const LocalSurfaceId& last_created_local_surface_id = last_created_surface_id_.local_surface_id(); - bool last_surface_has_dependent_frame = - prev_surface && prev_surface->HasDependentFrame(); bool child_initiated_synchronization_event = last_created_local_surface_id.is_valid() && @@ -485,14 +485,6 @@ return SubmitResult::SURFACE_ID_DECREASED; } - // If the last Surface doesn't have a dependent frame, and this frame - // corresponds to a child-initiated synchronization event then defer this - // Surface until a dependent frame arrives. This throttles child submission - // of CompositorFrames to the parent's embedding rate. - const bool block_activation_on_parent = - child_initiated_synchronization_event && - !last_surface_has_dependent_frame; - // Don't recreate a surface that was previously evicted. Drop the // CompositorFrame and return all its resources. if (IsEvicted(local_surface_id)) { @@ -502,7 +494,7 @@ } current_surface = surface_manager_->CreateSurface( - weak_factory_.GetWeakPtr(), surface_info, block_activation_on_parent); + weak_factory_.GetWeakPtr(), surface_info); if (!current_surface) { TRACE_EVENT_INSTANT0("viz", "Surface belongs to another client", TRACE_EVENT_SCOPE_THREAD); @@ -788,17 +780,16 @@ if (!last_activated_surface_id_.is_valid()) return true; - Surface* surface = - surface_manager_->GetSurfaceForId(last_activated_surface_id_); - // If client has not submitted any frames, or the first frame submitted is - // yet to be embedded, then allow the begin-frame to be dispatched to the - // client. - if (!surface || !surface->seen_first_surface_embedding()) + // We should never throttle BeginFrames if there is another client waiting for + // this client to submit a frame. + if (surface_manager_->HasBlockedEmbedder(frame_sink_id_)) return true; - // If the embedded surface doesn't have an active frame, send begin frame. - if (!surface->HasActiveFrame()) - return true; + Surface* surface = + surface_manager_->GetSurfaceForId(last_activated_surface_id_); + + DCHECK(surface); + DCHECK(surface->HasActiveFrame()); uint64_t active_frame_index = surface->GetActiveFrameIndex(); @@ -807,13 +798,8 @@ // must be at least as large as our last drawn frame index. DCHECK_GE(active_frame_index, last_drawn_frame_index_); - // Determine the number of undrawn frames. If this is below our limit, send - // begin frame. Limit must be at least 1, as the relative ordering of - // renderer / browser frame submissions allows us to have one outstanding - // undrawn frame under normal operation. - constexpr uint64_t undrawn_frame_limit = 1; uint64_t num_undrawn_frames = active_frame_index - last_drawn_frame_index_; - if (num_undrawn_frames <= undrawn_frame_limit) + if (num_undrawn_frames <= kUndrawnFrameLimit) return true; // Send begin-frames if the previous begin-frame was sent more than 1 second
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h index d9878f58..a6b5fd83 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -60,7 +60,13 @@ const gfx::Rect& damage_rect, base::TimeTicks expected_display_time)>; - static const uint64_t kFrameIndexStart = 2; + static constexpr uint64_t kFrameIndexStart = 2; + + // Determines maximum number of allowed undrawn frames. Once this limit is + // exceeded, we throttle sBeginFrames to 1 per second. Limit must be at least + // 1, as the relative ordering of renderer / browser frame submissions allows + // us to have one outstanding undrawn frame under normal operation. + static constexpr uint32_t kUndrawnFrameLimit = 3; CompositorFrameSinkSupport(mojom::CompositorFrameSinkClient* client, FrameSinkManagerImpl* frame_sink_manager, @@ -288,7 +294,7 @@ // TODO(crbug.com/754872): Remove once tab capture has moved into VIZ. AggregatedDamageCallback aggregated_damage_callback_; - uint64_t last_frame_index_ = kFrameIndexStart; + uint64_t last_frame_index_ = kFrameIndexStart - 1; // The video capture clients hooking into this instance to observe frame // begins and damage, and then make CopyOutputRequests on the appropriate
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc index 8ec698e6..14ab075 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -605,30 +605,6 @@ mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); EXPECT_EQ(SubmitResult::ACCEPTED, result); - // Since the Surface corresponding to |local_surface_id1| was not a dependency - // anywhere then the Surface corresponding to |local_surface_id2| will not - // activate until it becomes a dependency. - Surface* last_created_surface = support->GetLastCreatedSurfaceForTesting(); - EXPECT_EQ(local_surface_id2, - last_created_surface->surface_id().local_surface_id()); - EXPECT_FALSE(last_created_surface->HasActiveFrame()); - - SurfaceId surface_id2(kAnotherArbitraryFrameSinkId, local_surface_id2); - auto frame = - CompositorFrameBuilder() - .AddDefaultRenderPass() - .SetActivationDependencies({surface_id2}) - .SetReferencedSurfaces({SurfaceRange(base::nullopt, surface_id2)}) - .Build(); - result = support_->MaybeSubmitCompositorFrame( - local_surface_id_, std::move(frame), base::nullopt, 0, - mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); - EXPECT_EQ(SubmitResult::ACCEPTED, result); - - // Submitting a CompositorFrame to the parent FrameSink with a dependency on - // |local_surface_id2| causes that Surface's CompositorFrame to activate. - EXPECT_TRUE(last_created_surface->HasActiveFrame()); - // LocalSurfaceId(7, 2): Parent-initiated synchronization. result = support->MaybeSubmitCompositorFrame( local_surface_id3, MakeDefaultCompositorFrame(), base::nullopt, 0,
diff --git a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc index d38c3b4..17eb480 100644 --- a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc +++ b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
@@ -2042,7 +2042,8 @@ child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), MakeDefaultCompositorFrame()); EXPECT_TRUE(parent_surface()->HasActiveFrame()); - EXPECT_EQ(3u, parent_surface()->GetActiveFrameIndex()); + uint64_t expected_index = CompositorFrameSinkSupport::kFrameIndexStart; + EXPECT_EQ(expected_index, parent_surface()->GetActiveFrameIndex()); } // This test verifies that SurfaceManager::GetLatestInFlightSurface returns @@ -2126,9 +2127,7 @@ // Verify that there is a temporary reference for child_id3. EXPECT_TRUE(HasTemporaryReference(child_id3)); - // The surface corresponding to |child_id3| will not be activated until - // a parent embeds it because it's a child initiated synchronization. - EXPECT_EQ(GetSurfaceForId(child_id2), + EXPECT_EQ(GetSurfaceForId(child_id3), GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); parent_support().SubmitCompositorFrame( @@ -2136,8 +2135,6 @@ MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, std::vector<TransferableResource>())); - EXPECT_EQ(GetSurfaceForId(child_id3), - GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id3)); // If the primary surface is active, we return it. @@ -2831,291 +2828,6 @@ EXPECT_EQ(parent_id, parent_support().last_activated_surface_id()); } -// If a parent CompositorFrame embeds a child Surface newer than the throttled -// child then the throttled child surface is immediately unthrottled. This -// unblocks the child to make progress to catch up with the parent. -TEST_F(SurfaceSynchronizationTest, - ParentEmbeddingFutureChildUnblocksCurrentChildSurface) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); - const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 1, 3); - - // |child_id1| Surface should immediately activate. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface1 = GetSurfaceForId(child_id1); - ASSERT_NE(nullptr, child_surface1); - EXPECT_FALSE(child_surface1->HasPendingFrame()); - EXPECT_TRUE(child_surface1->HasActiveFrame()); - - // |child_id2| Surface should not activate because |child_id1| was never - // added as a dependency by a parent. - child_support1().SubmitCompositorFrame( - child_id2.local_surface_id(), - MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), - std::vector<TransferableResource>(), - MakeDeadline(1u))); - Surface* child_surface2 = GetSurfaceForId(child_id2); - ASSERT_NE(nullptr, child_surface2); - EXPECT_TRUE(child_surface2->HasPendingFrame()); - EXPECT_FALSE(child_surface2->HasActiveFrame()); - EXPECT_TRUE(child_surface2->has_deadline()); - - FrameDeadline deadline = MakeDefaultDeadline(); - base::TimeTicks deadline_wall_time = deadline.ToWallTime(); - EXPECT_EQ(deadline_wall_time, child_surface2->deadline_for_testing()); - - // The parent finally embeds a child surface that hasn't arrived which - // activates |child_id2|'s Surface in order for the child to make forward - // progress. - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, - std::vector<TransferableResource>(), - MakeDefaultDeadline())); - - EXPECT_FALSE(child_surface2->HasPendingFrame()); - EXPECT_TRUE(child_surface2->HasActiveFrame()); -} - -// A child surface can be blocked on its own activation dependencies and on a -// parent embedding it as an activation dependency. Even if a child's activation -// dependencies arrive, it may not activate until it is embedded. -TEST_F(SurfaceSynchronizationTest, ChildBlockedOnDependenciesAndParent) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); - const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink2, 1); - - // |child_id1| Surface should immediately activate. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface1 = GetSurfaceForId(child_id1); - ASSERT_NE(nullptr, child_surface1); - EXPECT_FALSE(child_surface1->HasPendingFrame()); - EXPECT_TRUE(child_surface1->HasActiveFrame()); - - // |child_id2| Surface should not activate because |child_id1| was never - // added as a dependency by a parent AND it depends on |child_id3| which has - // not yet arrived. - child_support1().SubmitCompositorFrame( - child_id2.local_surface_id(), - MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, - std::vector<TransferableResource>(), - MakeDefaultDeadline())); - Surface* child_surface2 = GetSurfaceForId(child_id2); - ASSERT_NE(nullptr, child_surface2); - EXPECT_TRUE(child_surface2->HasPendingFrame()); - EXPECT_FALSE(child_surface2->HasActiveFrame()); - EXPECT_THAT(child_surface2->activation_dependencies(), - UnorderedElementsAre(child_id3)); - - // |child_id2|'s dependency has arrived but |child_id2| Surface has not - // activated because it is still throttled by its parent. It will not - // activate until its parent arrives. - child_support2().SubmitCompositorFrame(child_id3.local_surface_id(), - MakeDefaultCompositorFrame()); - EXPECT_THAT(child_surface2->activation_dependencies(), IsEmpty()); - EXPECT_TRUE(child_surface2->HasPendingFrame()); - EXPECT_FALSE(child_surface2->HasActiveFrame()); - - // The parent finally embeds a |child_id2| which activates |child_id2|'s - // Surface. - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id2}, {SurfaceRange(base::nullopt, child_id2)}, - std::vector<TransferableResource>(), - MakeDefaultDeadline())); - - EXPECT_FALSE(child_surface2->HasPendingFrame()); - EXPECT_TRUE(child_surface2->HasActiveFrame()); -} - -// Similar to the previous test, a child surface can be blocked on its own -// activation dependencies and on a parent embedding it as an activation -// dependency. In this case, the parent CompositorFrame arrives first, and then -// the activation dependency. -TEST_F(SurfaceSynchronizationTest, ChildBlockedOnDependenciesAndParent2) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); - const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink2, 1); - - // |child_id1| Surface should immediately activate. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface1 = GetSurfaceForId(child_id1); - ASSERT_NE(nullptr, child_surface1); - EXPECT_FALSE(child_surface1->HasPendingFrame()); - EXPECT_TRUE(child_surface1->HasActiveFrame()); - - // |child_id2| Surface should not activate because |child_id1| was never - // added as a dependency by a parent AND it depends on |child_id3| which has - // not yet arrived. - child_support1().SubmitCompositorFrame( - child_id2.local_surface_id(), - MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, - std::vector<TransferableResource>(), - MakeDefaultDeadline())); - Surface* child_surface2 = GetSurfaceForId(child_id2); - ASSERT_NE(nullptr, child_surface2); - EXPECT_TRUE(child_surface2->HasPendingFrame()); - EXPECT_FALSE(child_surface2->HasActiveFrame()); - EXPECT_THAT(child_surface2->activation_dependencies(), - UnorderedElementsAre(child_id3)); - EXPECT_FALSE(child_surface2->HasDependentFrame()); - - // The parent embeds |child_id2| but |child_id2|'s Surface cannot activate - // until its dependencies arrive. - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id2}, {SurfaceRange(base::nullopt, child_id2)}, - std::vector<TransferableResource>(), - MakeDefaultDeadline())); - EXPECT_TRUE(child_surface2->HasPendingFrame()); - EXPECT_FALSE(child_surface2->HasActiveFrame()); - EXPECT_THAT(child_surface2->activation_dependencies(), - UnorderedElementsAre(child_id3)); - EXPECT_TRUE(child_surface2->HasDependentFrame()); - - // |child_id2|'s dependency has arrived and |child_id2|'s Surface finally - // activates. - child_support2().SubmitCompositorFrame(child_id3.local_surface_id(), - MakeDefaultCompositorFrame()); - EXPECT_THAT(child_surface2->activation_dependencies(), IsEmpty()); - EXPECT_FALSE(child_surface2->HasPendingFrame()); - EXPECT_TRUE(child_surface2->HasActiveFrame()); -} - -// This test verifies that if a child-initiated synchronization is blocked -// on a parent, then the frame will still activate after a deadline passes. -TEST_F(SurfaceSynchronizationTest, ChildBlockedOnParentActivatesAfterDeadline) { - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); - - // |child_id1| Surface should immediately activate. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface1 = GetSurfaceForId(child_id1); - ASSERT_NE(nullptr, child_surface1); - EXPECT_FALSE(child_surface1->HasPendingFrame()); - EXPECT_TRUE(child_surface1->HasActiveFrame()); - EXPECT_FALSE(child_surface1->HasDependentFrame()); - EXPECT_FALSE(child_surface1->has_deadline()); - - // |child_id2| Surface should not activate because |child_id1| was never - // added as a dependency by a parent. - child_support1().SubmitCompositorFrame( - child_id2.local_surface_id(), - MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), - std::vector<TransferableResource>(), - MakeDefaultDeadline())); - Surface* child_surface2 = GetSurfaceForId(child_id2); - ASSERT_NE(nullptr, child_surface2); - EXPECT_TRUE(child_surface2->HasPendingFrame()); - EXPECT_FALSE(child_surface2->HasActiveFrame()); - EXPECT_FALSE(child_surface2->HasDependentFrame()); - EXPECT_TRUE(child_surface2->has_deadline()); - - for (int i = 0; i < 3; ++i) { - SendNextBeginFrame(); - // There is still a looming deadline! Eeek! - EXPECT_TRUE(child_surface2->HasPendingFrame()); - EXPECT_FALSE(child_surface2->HasActiveFrame()); - EXPECT_FALSE(child_surface2->HasDependentFrame()); - EXPECT_TRUE(child_surface2->has_deadline()); - } - - SendNextBeginFrame(); - - EXPECT_FALSE(child_surface2->HasPendingFrame()); - EXPECT_TRUE(child_surface2->HasActiveFrame()); - // We pretend this is true so that subsequent CompositorFrames to the same - // surface do not block on the parent again. - EXPECT_TRUE(child_surface2->HasDependentFrame()); - EXPECT_FALSE(child_surface2->has_deadline()); -} - -// This test verifies that if a child-initiated synchronization is initiated -// with a deadline in the past, then the frame will immediately activate and -// be marked as having a dependent frame so that it does not block again in -// the future. -TEST_F(SurfaceSynchronizationTest, ChildBlockedOnParentDeadlineInPast) { - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); - - // |child_id1| Surface should immediately activate. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface1 = GetSurfaceForId(child_id1); - ASSERT_NE(nullptr, child_surface1); - EXPECT_FALSE(child_surface1->HasPendingFrame()); - EXPECT_TRUE(child_surface1->HasActiveFrame()); - EXPECT_FALSE(child_surface1->HasDependentFrame()); - EXPECT_FALSE(child_surface1->has_deadline()); - - // Pick a deadline in the near future. - FrameDeadline deadline = MakeDeadline(1u); - - // Advance time beyond the |deadline| above. - SendLateBeginFrame(); - - // |child_id2| Surface should activate because it was submitted with a - // deadline in the past. - child_support1().SubmitCompositorFrame( - child_id2.local_surface_id(), - MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), - std::vector<TransferableResource>(), deadline)); - Surface* child_surface2 = GetSurfaceForId(child_id2); - ASSERT_NE(nullptr, child_surface2); - EXPECT_FALSE(child_surface2->HasPendingFrame()); - EXPECT_TRUE(child_surface2->HasActiveFrame()); - EXPECT_FALSE(child_surface2->has_deadline()); - - EXPECT_TRUE(child_surface2->HasDependentFrame()); -} - -// A child may submit CompositorFrame corresponding to a child-initiated -// synchronization event followed by a CompositorFrame corresponding to a -// parent-initiated synchronization event. -TEST_F(SurfaceSynchronizationTest, - ParentInitiatedAfterChildInitiatedSynchronization) { - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); - // Child-initiated synchronization event: - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); - // Parent-initiated synchronizaton event: - const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 2, 2); - - // |child_id1| Surface should immediately activate. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface1 = GetSurfaceForId(child_id1); - ASSERT_NE(nullptr, child_surface1); - EXPECT_FALSE(child_surface1->HasPendingFrame()); - EXPECT_TRUE(child_surface1->HasActiveFrame()); - - // |child_id2| Surface should not activate because |child_id1| was never - // added as a dependency by a parent. - child_support1().SubmitCompositorFrame(child_id2.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface2 = GetSurfaceForId(child_id2); - ASSERT_NE(nullptr, child_surface2); - EXPECT_TRUE(child_surface2->HasPendingFrame()); - EXPECT_FALSE(child_surface2->HasActiveFrame()); - EXPECT_TRUE(child_surface2->has_deadline()); - - // |child_id3| Surface should activate immediately because it corresponds to a - // parent-initiated synchronization event. - child_support1().SubmitCompositorFrame(child_id3.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface3 = GetSurfaceForId(child_id3); - ASSERT_NE(nullptr, child_surface3); - EXPECT_FALSE(child_surface3->HasPendingFrame()); - EXPECT_TRUE(child_surface3->HasActiveFrame()); - EXPECT_FALSE(IsMarkedForDestruction(child_id3)); -} - TEST_F(SurfaceSynchronizationTest, EvictSurface) { const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); // Child-initiated synchronization event: @@ -3146,159 +2858,6 @@ EXPECT_FALSE(IsMarkedForDestruction(child_id3)); } -// If a parent CompositorFrame is blocked on the child, don't throttle child's -// surface to avoid a deadlock. In this variation of the test, parent's -// CompositorFrame arrives first. -TEST_F(SurfaceSynchronizationTest, ChildNotThrottledWhenParentBlocked1) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); - const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 2, 2); - const SurfaceId child_id4 = MakeSurfaceId(kChildFrameSink1, 2, 3); - const SurfaceId child_id5 = MakeSurfaceId(kChildFrameSink1, 2, 4); - - // The parent embeds |child_surface3|. This should avoid the child getting - // throttled when it submits to |child_id2|. - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, - std::vector<TransferableResource>(), - MakeDefaultDeadline())); - - // |child_id1| Surface should immediately activate because it's the child's - // first surface. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface1 = GetSurfaceForId(child_id1); - ASSERT_NE(nullptr, child_surface1); - EXPECT_FALSE(child_surface1->HasPendingFrame()); - EXPECT_TRUE(child_surface1->HasActiveFrame()); - - // |child_id2| would normally get throttled, but in this case it shouldn't - // because the parent is blocked. - child_support1().SubmitCompositorFrame( - child_id2.local_surface_id(), - MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), - std::vector<TransferableResource>(), - MakeDeadline(1u))); - Surface* child_surface2 = GetSurfaceForId(child_id2); - ASSERT_NE(nullptr, child_surface2); - EXPECT_FALSE(child_surface2->HasPendingFrame()); - EXPECT_TRUE(child_surface2->HasActiveFrame()); - - // After this submission, the parent will be unblocked. - Surface* parent_surface = GetSurfaceForId(parent_id); - ASSERT_NE(nullptr, parent_surface); - EXPECT_TRUE(parent_surface->HasPendingFrame()); - EXPECT_FALSE(parent_surface->HasActiveFrame()); - child_support1().SubmitCompositorFrame(child_id3.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface3 = GetSurfaceForId(child_id3); - ASSERT_NE(nullptr, child_surface3); - EXPECT_FALSE(child_surface3->HasPendingFrame()); - EXPECT_TRUE(child_surface3->HasActiveFrame()); - EXPECT_FALSE(parent_surface->HasPendingFrame()); - EXPECT_TRUE(parent_surface->HasActiveFrame()); - - // This is the first surface after the parent got unblocked. It will not get - // throttled. - child_support1().SubmitCompositorFrame(child_id4.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface4 = GetSurfaceForId(child_id4); - ASSERT_NE(nullptr, child_surface4); - EXPECT_FALSE(child_surface4->HasPendingFrame()); - EXPECT_TRUE(child_surface4->HasActiveFrame()); - - // This is the second surface after the parent got unblocked. It will get - // throttled. - child_support1().SubmitCompositorFrame(child_id5.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface5 = GetSurfaceForId(child_id5); - ASSERT_NE(nullptr, child_surface5); - EXPECT_TRUE(child_surface5->HasPendingFrame()); - EXPECT_FALSE(child_surface5->HasActiveFrame()); -} - -// If a parent CompositorFrame is blocked on the child, don't throttle child's -// surface to avoid a deadlock. In this variation of the test, child's -// CompositorFrame arrives first. -TEST_F(SurfaceSynchronizationTest, ChildNotThrottledWhenParentBlocked2) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); - const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 2, 1); - const SurfaceId child_id4 = MakeSurfaceId(kChildFrameSink1, 2, 2); - const SurfaceId child_id5 = MakeSurfaceId(kChildFrameSink1, 2, 3); - const SurfaceId child_id6 = MakeSurfaceId(kChildFrameSink1, 2, 4); - - // |child_id1| Surface should immediately activate. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface1 = GetSurfaceForId(child_id1); - ASSERT_NE(nullptr, child_surface1); - EXPECT_FALSE(child_surface1->HasPendingFrame()); - EXPECT_TRUE(child_surface1->HasActiveFrame()); - - // |child_id2| Surface should not activate because |child_id1| was never - // added as a dependency by a parent. - child_support1().SubmitCompositorFrame( - child_id2.local_surface_id(), - MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), - std::vector<TransferableResource>(), - MakeDeadline(1u))); - Surface* child_surface2 = GetSurfaceForId(child_id2); - ASSERT_NE(nullptr, child_surface2); - EXPECT_TRUE(child_surface2->HasPendingFrame()); - EXPECT_FALSE(child_surface2->HasActiveFrame()); - EXPECT_TRUE(child_surface2->has_deadline()); - - FrameDeadline deadline = MakeDefaultDeadline(); - base::TimeTicks deadline_wall_time = deadline.ToWallTime(); - EXPECT_EQ(deadline_wall_time, child_surface2->deadline_for_testing()); - - // The parent gets blocked on the child. The child should get unthrottled to - // avoid deadlocks. - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, - std::vector<TransferableResource>(), - MakeDefaultDeadline())); - EXPECT_FALSE(child_surface2->HasPendingFrame()); - EXPECT_TRUE(child_surface2->HasActiveFrame()); - - // After this submission, the parent will be unblocked. - Surface* parent_surface = GetSurfaceForId(parent_id); - ASSERT_NE(nullptr, parent_surface); - EXPECT_TRUE(parent_surface->HasPendingFrame()); - EXPECT_FALSE(parent_surface->HasActiveFrame()); - child_support1().SubmitCompositorFrame(child_id4.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface4 = GetSurfaceForId(child_id4); - ASSERT_NE(nullptr, child_surface4); - EXPECT_FALSE(child_surface4->HasPendingFrame()); - EXPECT_TRUE(child_surface4->HasActiveFrame()); - EXPECT_FALSE(parent_surface->HasPendingFrame()); - EXPECT_TRUE(parent_surface->HasActiveFrame()); - - // This is the first surface after the parent got unblocked. It will not get - // throttled. - child_support1().SubmitCompositorFrame(child_id5.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface5 = GetSurfaceForId(child_id5); - ASSERT_NE(nullptr, child_surface5); - EXPECT_FALSE(child_surface5->HasPendingFrame()); - EXPECT_TRUE(child_surface5->HasActiveFrame()); - - // This is the second surface after the parent got unblocked. It will get - // throttled. - child_support1().SubmitCompositorFrame(child_id6.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface6 = GetSurfaceForId(child_id6); - ASSERT_NE(nullptr, child_surface6); - EXPECT_TRUE(child_surface6->HasPendingFrame()); - EXPECT_FALSE(child_surface6->HasActiveFrame()); -} - // Tests that in cases where a pending-deletion surface (surface A) is // activated during anothother surface (surface B)'s deletion, we don't attempt // to delete surface A twice. @@ -3306,6 +2865,7 @@ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); // Child-initiated synchronization event: const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); + const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); // Submit a CompositorFrame to |child_id1|. child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), @@ -3318,8 +2878,10 @@ EXPECT_TRUE(child_surface1->HasActiveFrame()); // Submit a CompositorFrame to |child_id2|. - child_support1().SubmitCompositorFrame(child_id2.local_surface_id(), - MakeDefaultCompositorFrame()); + child_support1().SubmitCompositorFrame( + child_id2.local_surface_id(), + MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(), + std::vector<TransferableResource>())); // Child 2 should not yet be active. Surface* child_surface2 = GetSurfaceForId(child_id2); @@ -3488,72 +3050,6 @@ EXPECT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(child_id2)); } -// This test verifies that the child gets unthrottled when the parent embeds the -// second last surface. https://crbug.com/898460 -TEST_F(SurfaceSynchronizationTest, - ChildUnthrottledWhenSecondLastSurfaceEmbedded) { - const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); - const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); - const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); - const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 1, 3); - const SurfaceId child_id4 = MakeSurfaceId(kChildFrameSink1, 1, 4); - - // |child_id1| Surface should immediately activate because one unembedded - // surface is allowed. - child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), - MakeDefaultCompositorFrame()); - Surface* child_surface1 = GetSurfaceForId(child_id1); - ASSERT_NE(nullptr, child_surface1); - EXPECT_FALSE(child_surface1->HasPendingFrame()); - EXPECT_TRUE(child_surface1->HasActiveFrame()); - - // |child_id2| Surface should not activate because now there are two surfaces - // not embedded by the parent makes child throttling kick in. - child_support1().SubmitCompositorFrame( - child_id2.local_surface_id(), - MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), - std::vector<TransferableResource>(), - MakeDeadline(1u))); - Surface* child_surface2 = GetSurfaceForId(child_id2); - ASSERT_NE(nullptr, child_surface2); - EXPECT_TRUE(child_surface2->HasPendingFrame()); - EXPECT_FALSE(child_surface2->HasActiveFrame()); - EXPECT_TRUE(child_surface2->has_deadline()); - - // The parent embeds |child_id1| and blocks. Both |child_id2| should activate - // because now again there is only one surface not embedded by the parent. - parent_support().SubmitCompositorFrame( - parent_id.local_surface_id(), - MakeCompositorFrame({child_id1}, {SurfaceRange(base::nullopt, child_id1)}, - std::vector<TransferableResource>(), - MakeDefaultDeadline())); - EXPECT_FALSE(child_surface2->HasPendingFrame()); - EXPECT_TRUE(child_surface2->HasActiveFrame()); - - // The child submits to |child_id3|. Throttling should still not kick in due - // to https://crbug.com/951992. - child_support1().SubmitCompositorFrame( - child_id3.local_surface_id(), - MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), - std::vector<TransferableResource>(), - MakeDeadline(1u))); - Surface* child_surface3 = GetSurfaceForId(child_id3); - ASSERT_NE(nullptr, child_surface3); - EXPECT_FALSE(child_surface3->HasPendingFrame()); - EXPECT_TRUE(child_surface3->HasActiveFrame()); - - // The child submits to |child_id4|. Throttling should kick in. - child_support1().SubmitCompositorFrame( - child_id4.local_surface_id(), - MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), - std::vector<TransferableResource>(), - MakeDeadline(1u))); - Surface* child_surface4 = GetSurfaceForId(child_id4); - ASSERT_NE(nullptr, child_surface4); - EXPECT_TRUE(child_surface4->HasPendingFrame()); - EXPECT_FALSE(child_surface4->HasActiveFrame()); -} - // Verifies that the value of last active surface is correct after embed token // changes. https://crbug.com/967012 TEST_F(SurfaceSynchronizationTest,
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc index 47e3bd2..eed28b34 100644 --- a/components/viz/service/surfaces/surface.cc +++ b/components/viz/service/surfaces/surface.cc
@@ -29,12 +29,10 @@ Surface::Surface(const SurfaceInfo& surface_info, SurfaceManager* surface_manager, SurfaceAllocationGroup* allocation_group, - base::WeakPtr<SurfaceClient> surface_client, - bool block_activation_on_parent) + base::WeakPtr<SurfaceClient> surface_client) : surface_info_(surface_info), surface_manager_(surface_manager), surface_client_(std::move(surface_client)), - block_activation_on_parent_(block_activation_on_parent), allocation_group_(allocation_group), weak_factory_(this) { TRACE_EVENT_ASYNC_BEGIN1(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"), @@ -167,18 +165,6 @@ } } -void Surface::OnSurfaceDependencyAdded() { - if (seen_first_surface_dependency_) - return; - - seen_first_surface_dependency_ = true; - if (!activation_dependencies_.empty() || !pending_frame_data_) - return; - - // All blockers have been cleared. The surface can be activated now. - ActivatePendingFrame(); -} - void Surface::SetIsFallbackAndMaybeActivate() { is_fallback_ = true; if (HasPendingFrame()) @@ -224,16 +210,7 @@ // regardless of whether it's pending or active. surface_client_->ReceiveFromChild(frame.resource_list); - if (!seen_first_surface_dependency_) { - // We should not throttle this client if there is another client blocked on - // it, in order to avoid deadlocks. - seen_first_surface_dependency_ = allocation_group_->HasBlockedEmbedder(); - } - - bool block_activation = - block_activation_on_parent_ && !seen_first_surface_dependency_; - - if (!block_activation && activation_dependencies_.empty()) { + if (activation_dependencies_.empty()) { // If there are no blockers, then immediately activate the frame. ActivateFrame(FrameData(std::move(frame), frame_index), base::nullopt); } else { @@ -283,9 +260,7 @@ DCHECK(activation_dependencies_.count(activation_dependency)); activation_dependencies_.erase(activation_dependency); blocking_allocation_groups_.erase(group); - bool block_activation = - block_activation_on_parent_ && !seen_first_surface_dependency_; - if (block_activation || !activation_dependencies_.empty()) + if (!activation_dependencies_.empty()) return; // All blockers have been cleared. The surface can be activated now. ActivatePendingFrame(); @@ -299,11 +274,6 @@ // of blockers. activation_dependencies_.clear(); - // We treat an activation (by deadline) as being the equivalent of a parent - // embedding the surface in order to avoid blocking future frames to the same - // surface. - seen_first_surface_dependency_ = true; - ActivatePendingFrame(); } @@ -479,13 +449,9 @@ const FrameDeadline& deadline = current_frame.metadata.deadline; uint32_t deadline_in_frames = deadline.deadline_in_frames(); - bool block_activation = - block_activation_on_parent_ && !seen_first_surface_dependency_; - // If no default deadline is available then all deadlines are treated as // effectively infinite deadlines. - if (!default_deadline || deadline.use_default_lower_bound_deadline() || - block_activation) { + if (!default_deadline || deadline.use_default_lower_bound_deadline()) { deadline_in_frames = std::max( deadline_in_frames, default_deadline.value_or(std::numeric_limits<uint32_t>::max())); @@ -503,12 +469,9 @@ blocking_allocation_groups_.clear(); activation_dependencies_.clear(); - // If the client has specified a deadline of zero and we don't need to block - // on the parent, there is no need to figure out the activation dependencies, - // since the frame will activate immediately. - bool block_activation = - block_activation_on_parent_ && !seen_first_surface_dependency_; - if (!block_activation && current_frame.metadata.deadline.IsZero()) + // If the client has specified a deadline of zero, there is no need to figure + // out the activation dependencies since the frame will activate immediately. + if (current_frame.metadata.deadline.IsZero()) return; std::vector<SurfaceAllocationGroup*> new_blocking_allocation_groups;
diff --git a/components/viz/service/surfaces/surface.h b/components/viz/service/surfaces/surface.h index ce9c8d94..8c9ee36 100644 --- a/components/viz/service/surfaces/surface.h +++ b/components/viz/service/surfaces/surface.h
@@ -83,8 +83,7 @@ Surface(const SurfaceInfo& surface_info, SurfaceManager* surface_manager, SurfaceAllocationGroup* allocation_group, - base::WeakPtr<SurfaceClient> surface_client, - bool block_activation_on_parent); + base::WeakPtr<SurfaceClient> surface_client); ~Surface(); void SetDependencyDeadline( @@ -120,10 +119,6 @@ return surface_client_ ? surface_client_->NeedsSyncTokens() : false; } - bool block_activation_on_parent() const { - return block_activation_on_parent_; - } - // Returns false if |frame| is invalid. // |frame_rejected_callback| will be called once if the frame will not be // displayed. @@ -200,10 +195,6 @@ return HasActiveFrame() && !active_frame_data_->frame_acked; } - // Returns true if at any point, another Surface's CompositorFrame has - // depended on this Surface. - bool HasDependentFrame() const { return seen_first_surface_dependency_; } - bool seen_first_surface_embedding() const { return seen_first_surface_embedding_; } @@ -217,9 +208,6 @@ // referenced SurfaceRange. void OnChildActivatedForActiveFrame(const SurfaceId& surface_id); - // Called when this surface is embedded by another Surface's CompositorFrame. - void OnSurfaceDependencyAdded(); - // Called when the embedder of this surface has been activated and therefore // this surface should activate too by deadline inheritance. void ActivatePendingFrameForInheritedDeadline(); @@ -312,13 +300,6 @@ base::Optional<FrameData> active_frame_data_; bool seen_first_frame_activation_ = false; bool seen_first_surface_embedding_ = false; - // Indicates whether another surface adds this surface as a dependency. When - // set to true, this surface will be unthrottled and the surface that is - // created after it will also not be throttled. - bool seen_first_surface_dependency_ = false; - // When false, this surface will not be subject to child throttling even if - // it's not embedded yet. - bool block_activation_on_parent_ = false; // A set of all valid SurfaceIds contained |last_surface_id_for_range_| to // avoid recompution.
diff --git a/components/viz/service/surfaces/surface_allocation_group.cc b/components/viz/service/surfaces/surface_allocation_group.cc index 75d414ef..3104b56 100644 --- a/components/viz/service/surfaces/surface_allocation_group.cc +++ b/components/viz/service/surfaces/surface_allocation_group.cc
@@ -241,25 +241,11 @@ auto it = FindLatestSurfaceUpTo(surface_id); if (it == surfaces_.end()) return; - // Notify that |*it| is now embedded. This might unblock |*it| if it was - // blocked due to child throttling. - (*it)->OnSurfaceDependencyAdded(); // If |surface_id| does not exist yet, notify the surface immediately prior to // it that it is a fallback. This might activate the surface immediately // because fallback surfaces never block. if ((*it)->surface_id() != surface_id) (*it)->SetIsFallbackAndMaybeActivate(); - // Since child throttling allows up to two unembedded surfaces, the surface - // immediately after |it| should not be throttled even if it is not embedded - // yet. - ++it; - if (it == surfaces_.end()) - return; - // This ensures the next surface has its seen_first_surface_dependency_ bit - // set so that throttling doesn't kick in until 3 surfaces after the surface - // that was just embedded. We see regression if throttling kicks in sooner. - // See https://crbug.com/951992. - (*it)->OnSurfaceDependencyAdded(); } } // namespace viz
diff --git a/components/viz/service/surfaces/surface_manager.cc b/components/viz/service/surfaces/surface_manager.cc index 17f9a1c..3f533bf 100644 --- a/components/viz/service/surfaces/surface_manager.cc +++ b/components/viz/service/surfaces/surface_manager.cc
@@ -103,8 +103,7 @@ Surface* SurfaceManager::CreateSurface( base::WeakPtr<SurfaceClient> surface_client, - const SurfaceInfo& surface_info, - bool block_activation_on_parent) { + const SurfaceInfo& surface_info) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(surface_info.is_valid()); DCHECK(surface_client); @@ -121,9 +120,8 @@ if (!allocation_group) return nullptr; - std::unique_ptr<Surface> surface = - std::make_unique<Surface>(surface_info, this, allocation_group, - surface_client, block_activation_on_parent); + std::unique_ptr<Surface> surface = std::make_unique<Surface>( + surface_info, this, allocation_group, surface_client); surface->SetDependencyDeadline( std::make_unique<SurfaceDependencyDeadline>(tick_clock_)); surface_map_[surface_info.id()] = std::move(surface); @@ -633,4 +631,17 @@ allocation_groups_need_garbage_collection_ = false; } +bool SurfaceManager::HasBlockedEmbedder( + const FrameSinkId& frame_sink_id) const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + auto it = frame_sink_id_to_allocation_groups_.find(frame_sink_id); + if (it == frame_sink_id_to_allocation_groups_.end()) + return false; + for (SurfaceAllocationGroup* group : it->second) { + if (group->HasBlockedEmbedder()) + return true; + } + return false; +} + } // namespace viz
diff --git a/components/viz/service/surfaces/surface_manager.h b/components/viz/service/surfaces/surface_manager.h index 230b699..bbe658bb 100644 --- a/components/viz/service/surfaces/surface_manager.h +++ b/components/viz/service/surfaces/surface_manager.h
@@ -79,8 +79,7 @@ // dependencies are satisfied, and it is not reachable from the root surface. // A temporary reference will be added to the new Surface. Surface* CreateSurface(base::WeakPtr<SurfaceClient> surface_client, - const SurfaceInfo& surface_info, - bool block_activation_on_parent); + const SurfaceInfo& surface_info); // Marks |surface_id| for destruction. The surface will get destroyed when // it's not reachable from the root or any other surface that is not marked @@ -194,6 +193,10 @@ // collection. void SetAllocationGroupsNeedGarbageCollection(); + // Returns whether there is any surface blocked on a surface from + // |frame_sink_id|. + bool HasBlockedEmbedder(const FrameSinkId& frame_sink_id) const; + private: friend class CompositorFrameSinkSupportTest; friend class FrameSinkManagerTest;
diff --git a/content/browser/android/selection/OWNERS b/content/browser/android/selection/OWNERS index 4943acc..395fd997 100644 --- a/content/browser/android/selection/OWNERS +++ b/content/browser/android/selection/OWNERS
@@ -1 +1 @@ -amaralp@chromium.org +ctzsm@chromium.org
diff --git a/content/browser/dom_storage/dom_storage_area.cc b/content/browser/dom_storage/dom_storage_area.cc index 590de95..4fdd70a7 100644 --- a/content/browser/dom_storage/dom_storage_area.cc +++ b/content/browser/dom_storage/dom_storage_area.cc
@@ -524,9 +524,7 @@ if (!GetCurrentCommitBatch()) { commit_batches_.emplace_front(CommitBatchHolder( CommitBatchHolder::TYPE_CURRENT_BATCH, new CommitBatch())); - BrowserThread::PostAfterStartupTask( - FROM_HERE, task_runner_, - base::BindOnce(&DOMStorageArea::StartCommitTimer, this)); + StartCommitTimer(); } return GetCurrentCommitBatch()->batch.get(); }
diff --git a/content/browser/dom_storage/storage_area_impl.cc b/content/browser/dom_storage/storage_area_impl.cc index 4d87523f..fd55c52 100644 --- a/content/browser/dom_storage/storage_area_impl.cc +++ b/content/browser/dom_storage/storage_area_impl.cc
@@ -724,10 +724,7 @@ DCHECK(database_); commit_batch_.reset(new CommitBatch()); - BrowserThread::PostAfterStartupTask( - FROM_HERE, base::ThreadTaskRunnerHandle::Get(), - base::BindOnce(&StorageAreaImpl::StartCommitTimer, - weak_ptr_factory_.GetWeakPtr())); + StartCommitTimer(); } void StorageAreaImpl::StartCommitTimer() {
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc index bfbc2ea..9994201 100644 --- a/content/browser/download/download_manager_impl.cc +++ b/content/browser/download/download_manager_impl.cc
@@ -549,6 +549,7 @@ params.frame_tree_node_id = RenderFrameHost::GetFrameTreeNodeIdForRoutingId( info.render_process_id, info.render_frame_id); + params.from_download_cross_origin_redirect = true; web_contents->GetController().LoadURLWithParams(params); } if (info.request_handle)
diff --git a/content/browser/download/download_resource_handler.cc b/content/browser/download/download_resource_handler.cc index 7cd3beec..493f622 100644 --- a/content/browser/download/download_resource_handler.cc +++ b/content/browser/download/download_resource_handler.cc
@@ -124,6 +124,7 @@ const std::vector<GURL> url_chain, const Referrer& referrer, bool has_user_gesture, + bool from_download_cross_origin_redirect, const ResourceRequestInfo::WebContentsGetter& wc_getter, int frame_tree_node_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -135,6 +136,8 @@ params.referrer = referrer; params.redirect_chain = url_chain; params.frame_tree_node_id = frame_tree_node_id; + params.from_download_cross_origin_redirect = + from_download_cross_origin_redirect; web_contents->GetController().LoadURLWithParams(params); } } @@ -210,6 +213,7 @@ Referrer::NetReferrerPolicyToBlinkReferrerPolicy( redirect_info.new_referrer_policy)), GetRequestInfo()->HasUserGesture(), + true /* from_download_cross_origin_redirect */, GetRequestInfo()->GetWebContentsGetterForRequest(), GetRequestInfo()->frame_tree_node_id())); controller->Cancel();
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc index f50c1283..031b30e 100644 --- a/content/browser/frame_host/navigation_controller_impl.cc +++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -3141,10 +3141,14 @@ // extra_headers in params are \n separated; NavigationRequests want \r\n. std::string extra_headers_crlf; base::ReplaceChars(params.extra_headers, "\n", "\r\n", &extra_headers_crlf); - return NavigationRequest::CreateBrowserInitiated( + + auto navigation_request = NavigationRequest::CreateBrowserInitiated( node, common_params, commit_params, !params.is_renderer_initiated, extra_headers_crlf, *frame_entry, entry, request_body, params.navigation_ui_data ? params.navigation_ui_data->Clone() : nullptr); + navigation_request->set_from_download_cross_origin_redirect( + params.from_download_cross_origin_redirect); + return navigation_request; } std::unique_ptr<NavigationRequest>
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc index 7c0ab24..86b9134 100644 --- a/content/browser/frame_host/navigation_handle_impl.cc +++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -452,6 +452,10 @@ return navigation_request_->navigation_entry_offset(); } +bool NavigationHandleImpl::FromDownloadCrossOriginRedirect() { + return navigation_request_->from_download_cross_origin_redirect(); +} + bool NavigationHandleImpl::IsSignedExchangeInnerResponse() { return navigation_request_->response() ? navigation_request_->response()
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h index 2a97d1a..3172fad 100644 --- a/content/browser/frame_host/navigation_handle_impl.h +++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -112,6 +112,7 @@ const base::Optional<url::Origin>& GetInitiatorOrigin() override; bool IsSameProcess() override; int GetNavigationEntryOffset() override; + bool FromDownloadCrossOriginRedirect() override; // Returns the NavigationRequest which owns this NavigationHandle. NavigationRequest* navigation_request() { return navigation_request_; }
diff --git a/content/browser/frame_host/navigation_handle_impl_unittest.cc b/content/browser/frame_host/navigation_handle_impl_unittest.cc index f9d0d4ba..1a5ba30 100644 --- a/content/browser/frame_host/navigation_handle_impl_unittest.cc +++ b/content/browser/frame_host/navigation_handle_impl_unittest.cc
@@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/macros.h" #include "base/optional.h" +#include "build/build_config.h" #include "content/browser/frame_host/navigation_request.h" #include "content/public/browser/navigation_throttle.h" #include "content/public/browser/ssl_status.h" @@ -306,9 +307,16 @@ navigation->GetNavigationHandle()->GetNetErrorCode()); } +// Flaky on Android. https://crbug.com/970815 +#if defined(OS_ANDROID) +#define MAYBE_CancelDeferredWillStart DISABLED_CancelDeferredWillStart +#else +#define MAYBE_CancelDeferredWillStart CancelDeferredWillStart +#endif + // Checks that a navigation deferred during WillStartRequest can be properly // cancelled. -TEST_F(NavigationHandleImplTest, CancelDeferredWillStart) { +TEST_F(NavigationHandleImplTest, MAYBE_CancelDeferredWillStart) { TestNavigationThrottle* test_throttle = CreateTestNavigationThrottle(NavigationThrottle::DEFER); EXPECT_EQ(NavigationRequest::INITIAL, state()); @@ -329,9 +337,16 @@ EXPECT_TRUE(call_counts_match(test_throttle, 1, 0, 0, 0)); } +// Flaky on Android. https://crbug.com/970815 +#if defined(OS_ANDROID) +#define MAYBE_CancelDeferredWillRedirect DISABLED_CancelDeferredWillRedirect +#else +#define MAYBE_CancelDeferredWillRedirect CancelDeferredWillRedirect +#endif + // Checks that a navigation deferred during WillRedirectRequest can be properly // cancelled. -TEST_F(NavigationHandleImplTest, CancelDeferredWillRedirect) { +TEST_F(NavigationHandleImplTest, MAYBE_CancelDeferredWillRedirect) { TestNavigationThrottle* test_throttle = CreateTestNavigationThrottle(NavigationThrottle::DEFER); EXPECT_EQ(NavigationRequest::INITIAL, state()); @@ -352,9 +367,16 @@ EXPECT_TRUE(call_counts_match(test_throttle, 0, 1, 0, 0)); } +// Flaky on Android. https://crbug.com/970815 +#if defined(OS_ANDROID) +#define MAYBE_CancelDeferredWillFail DISABLED_CancelDeferredWillFail +#else +#define MAYBE_CancelDeferredWillFail CancelDeferredWillFail +#endif + // Checks that a navigation deferred during WillFailRequest can be properly // cancelled. -TEST_F(NavigationHandleImplTest, CancelDeferredWillFail) { +TEST_F(NavigationHandleImplTest, MAYBE_CancelDeferredWillFail) { TestNavigationThrottle* test_throttle = CreateTestNavigationThrottle( TestNavigationThrottle::WILL_FAIL_REQUEST, NavigationThrottle::DEFER); EXPECT_EQ(NavigationRequest::INITIAL, state()); @@ -379,8 +401,17 @@ EXPECT_TRUE(call_counts_match(test_throttle, 1, 0, 1, 0)); } +// Flaky on Android. https://crbug.com/970815 +#if defined(OS_ANDROID) +#define MAYBE_CancelDeferredWillRedirectNoIgnore \ + DISABLED_CancelDeferredWillRedirectNoIgnore +#else +#define MAYBE_CancelDeferredWillRedirectNoIgnore \ + CancelDeferredWillRedirectNoIgnore +#endif + // Checks that a navigation deferred can be canceled and not ignored. -TEST_F(NavigationHandleImplTest, CancelDeferredWillRedirectNoIgnore) { +TEST_F(NavigationHandleImplTest, MAYBE_CancelDeferredWillRedirectNoIgnore) { TestNavigationThrottle* test_throttle = CreateTestNavigationThrottle(NavigationThrottle::DEFER); EXPECT_EQ(NavigationRequest::INITIAL, state()); @@ -401,9 +432,17 @@ EXPECT_TRUE(call_counts_match(test_throttle, 1, 0, 0, 0)); } +// Flaky on Android. https://crbug.com/970815 +#if defined(OS_ANDROID) +#define MAYBE_CancelDeferredWillFailNoIgnore \ + DISABLED_CancelDeferredWillFailNoIgnore +#else +#define MAYBE_CancelDeferredWillFailNoIgnore CancelDeferredWillFailNoIgnore +#endif + // Checks that a navigation deferred by WillFailRequest can be canceled and not // ignored. -TEST_F(NavigationHandleImplTest, CancelDeferredWillFailNoIgnore) { +TEST_F(NavigationHandleImplTest, MAYBE_CancelDeferredWillFailNoIgnore) { TestNavigationThrottle* test_throttle = CreateTestNavigationThrottle( TestNavigationThrottle::WILL_FAIL_REQUEST, NavigationThrottle::DEFER); EXPECT_EQ(NavigationRequest::INITIAL, state()); @@ -490,11 +529,21 @@ } // namespace +// Flaky on Android. https://crbug.com/970815 +#if defined(OS_ANDROID) +#define MAYBE_WillFailRequestCanAccessRenderFrameHost \ + DISABLED_WillFailRequestCanAccessRenderFrameHost +#else +#define MAYBE_WillFailRequestCanAccessRenderFrameHost \ + WillFailRequestCanAccessRenderFrameHost +#endif + // Verify that the NavigationHandle::GetRenderFrameHost() can be retrieved by a // throttle in WillFailRequest(), as well as after deferring the failure. This // is allowed, since at that point the final RenderFrameHost will have already // been chosen. See https://crbug.com/817881. -TEST_F(NavigationHandleImplTest, WillFailRequestCanAccessRenderFrameHost) { +TEST_F(NavigationHandleImplTest, + MAYBE_WillFailRequestCanAccessRenderFrameHost) { std::unique_ptr<ContentBrowserClient> client( new ThrottleTestContentBrowserClient); ContentBrowserClient* old_browser_client =
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h index 523b619..50e22d8 100644 --- a/content/browser/frame_host/navigation_request.h +++ b/content/browser/frame_host/navigation_request.h
@@ -406,6 +406,9 @@ return previous_url_; } + bool from_download_cross_origin_redirect() const { + return from_download_cross_origin_redirect_; + } #if defined(OS_ANDROID) // Returns a reference to |navigation_handle_| Java counterpart. It is used @@ -421,6 +424,11 @@ Referrer& sanitized_referrer() { return sanitized_referrer_; } + void set_from_download_cross_origin_redirect( + bool from_download_cross_origin_redirect) { + from_download_cross_origin_redirect_ = from_download_cross_origin_redirect; + } + // This should be a private method. The only valid reason to be used // outside of the class constructor is in the case of an initial history // navigation in a subframe. This allows a browser-initiated NavigationRequest @@ -833,6 +841,10 @@ bool was_redirected_ = false; + // Whether this navigation was triggered by a x-origin redirect following a + // prior (most likely <a download>) download attempt. + bool from_download_cross_origin_redirect_ = false; + // Used when SignedExchangeSubresourcePrefetch is enabled to hold the // prefetched signed exchanges. This is shared with the navigation initiator's // RenderFrameHostImpl. This also means that only the navigations that were
diff --git a/content/browser/gpu/gpu_data_manager_impl.cc b/content/browser/gpu/gpu_data_manager_impl.cc index 13c5feb..da6ef878 100644 --- a/content/browser/gpu/gpu_data_manager_impl.cc +++ b/content/browser/gpu/gpu_data_manager_impl.cc
@@ -25,12 +25,12 @@ private_->BlacklistWebGLForTesting(); } -gpu::GPUInfo GpuDataManagerImpl::GetGPUInfo() const { +gpu::GPUInfo GpuDataManagerImpl::GetGPUInfo() { base::AutoLock auto_lock(lock_); return private_->GetGPUInfo(); } -bool GpuDataManagerImpl::GpuAccessAllowed(std::string* reason) const { +bool GpuDataManagerImpl::GpuAccessAllowed(std::string* reason) { base::AutoLock auto_lock(lock_); return private_->GpuAccessAllowed(reason); } @@ -40,7 +40,7 @@ private_->RequestCompleteGpuInfoIfNeeded(); } -bool GpuDataManagerImpl::IsEssentialGpuInfoAvailable() const { +bool GpuDataManagerImpl::IsEssentialGpuInfoAvailable() { base::AutoLock auto_lock(lock_); return private_->IsEssentialGpuInfoAvailable(); } @@ -57,7 +57,7 @@ } void GpuDataManagerImpl::RequestVideoMemoryUsageStatsUpdate( - VideoMemoryUsageStatsCallback callback) const { + VideoMemoryUsageStatsCallback callback) { base::AutoLock auto_lock(lock_); private_->RequestVideoMemoryUsageStatsUpdate(std::move(callback)); } @@ -79,13 +79,13 @@ private_->DisableHardwareAcceleration(); } -bool GpuDataManagerImpl::HardwareAccelerationEnabled() const { +bool GpuDataManagerImpl::HardwareAccelerationEnabled() { base::AutoLock auto_lock(lock_); return private_->HardwareAccelerationEnabled(); } void GpuDataManagerImpl::AppendGpuCommandLine(base::CommandLine* command_line, - GpuProcessKind kind) const { + GpuProcessKind kind) { base::AutoLock auto_lock(lock_); private_->AppendGpuCommandLine(command_line, kind); }
diff --git a/content/browser/gpu/gpu_data_manager_impl.h b/content/browser/gpu/gpu_data_manager_impl.h index 9c3f898..dfbc831 100644 --- a/content/browser/gpu/gpu_data_manager_impl.h +++ b/content/browser/gpu/gpu_data_manager_impl.h
@@ -46,12 +46,12 @@ // GpuDataManager implementation. void BlacklistWebGLForTesting() override; - gpu::GPUInfo GetGPUInfo() const override; - bool GpuAccessAllowed(std::string* reason) const override; + gpu::GPUInfo GetGPUInfo() override; + bool GpuAccessAllowed(std::string* reason) override; void RequestCompleteGpuInfoIfNeeded() override; - bool IsEssentialGpuInfoAvailable() const override; + bool IsEssentialGpuInfoAvailable() override; void RequestVideoMemoryUsageStatsUpdate( - VideoMemoryUsageStatsCallback callback) const override; + VideoMemoryUsageStatsCallback callback) override; // TODO(kbr): the threading model for the GpuDataManagerObservers is // not well defined, and it's impossible for callers to correctly // delete observers from anywhere except in one of the observer's @@ -60,9 +60,9 @@ void AddObserver(GpuDataManagerObserver* observer) override; void RemoveObserver(GpuDataManagerObserver* observer) override; void DisableHardwareAcceleration() override; - bool HardwareAccelerationEnabled() const override; + bool HardwareAccelerationEnabled() override; void AppendGpuCommandLine(base::CommandLine* command_line, - GpuProcessKind kind) const override; + GpuProcessKind kind) override; void RequestGpuSupportedRuntimeVersion() const; bool GpuProcessStartAllowed() const;
diff --git a/content/browser/gpu/gpu_internals_ui.cc b/content/browser/gpu/gpu_internals_ui.cc index 6238102..76fa8b65 100644 --- a/content/browser/gpu/gpu_internals_ui.cc +++ b/content/browser/gpu/gpu_internals_ui.cc
@@ -407,7 +407,7 @@ return display_info; } -std::string GetProfileName(gpu::VideoCodecProfile profile) { +const char* GetProfileName(gpu::VideoCodecProfile profile) { switch (profile) { case gpu::VIDEO_CODEC_PROFILE_UNKNOWN: return "unknown"; @@ -457,6 +457,10 @@ return "dolby vision profile 5"; case gpu::DOLBYVISION_PROFILE7: return "dolby vision profile 7"; + case gpu::DOLBYVISION_PROFILE8: + return "dolby vision profile 8"; + case gpu::DOLBYVISION_PROFILE9: + return "dolby vision profile 9"; case gpu::THEORAPROFILE_ANY: return "theora"; case gpu::AV1PROFILE_PROFILE_MAIN: @@ -476,8 +480,8 @@ for (const auto& profile : gpu_info.video_decode_accelerator_capabilities.supported_profiles) { - std::string codec_string = base::StringPrintf( - "Decode %s", GetProfileName(profile.profile).c_str()); + std::string codec_string = + base::StringPrintf("Decode %s", GetProfileName(profile.profile)); std::string resolution_string = base::StringPrintf( "up to %s pixels %s", profile.max_resolution.ToString().c_str(), profile.encrypted_only ? "(encrypted)" : ""); @@ -486,8 +490,8 @@ for (const auto& profile : gpu_info.video_encode_accelerator_supported_profiles) { - std::string codec_string = base::StringPrintf( - "Encode %s", GetProfileName(profile.profile).c_str()); + std::string codec_string = + base::StringPrintf("Encode %s", GetProfileName(profile.profile)); std::string resolution_string = base::StringPrintf( "up to %s pixels and/or %.3f fps", profile.max_resolution.ToString().c_str(),
diff --git a/content/browser/host_zoom_map_impl.cc b/content/browser/host_zoom_map_impl.cc index d4a3e7ec..3ce022d 100644 --- a/content/browser/host_zoom_map_impl.cc +++ b/content/browser/host_zoom_map_impl.cc
@@ -150,7 +150,7 @@ } bool HostZoomMapImpl::HasZoomLevel(const std::string& scheme, - const std::string& host) const { + const std::string& host) { DCHECK_CURRENTLY_ON(BrowserThread::UI); auto scheme_iterator(scheme_host_zoom_levels_.find(scheme)); @@ -162,9 +162,8 @@ return base::ContainsKey(zoom_levels, host); } -double HostZoomMapImpl::GetZoomLevelForHostAndScheme( - const std::string& scheme, - const std::string& host) const { +double HostZoomMapImpl::GetZoomLevelForHostAndScheme(const std::string& scheme, + const std::string& host) { DCHECK_CURRENTLY_ON(BrowserThread::UI); auto scheme_iterator(scheme_host_zoom_levels_.find(scheme)); if (scheme_iterator != scheme_host_zoom_levels_.end()) { @@ -176,7 +175,7 @@ return GetZoomLevelForHost(host); } -HostZoomMap::ZoomLevelVector HostZoomMapImpl::GetAllZoomLevels() const { +HostZoomMap::ZoomLevelVector HostZoomMapImpl::GetAllZoomLevels() { DCHECK_CURRENTLY_ON(BrowserThread::UI); HostZoomMap::ZoomLevelVector result; result.reserve(host_zoom_levels_.size() + scheme_host_zoom_levels_.size()); @@ -266,7 +265,7 @@ zoom_level_changed_callbacks_.Notify(change); } -double HostZoomMapImpl::GetDefaultZoomLevel() const { +double HostZoomMapImpl::GetDefaultZoomLevel() { DCHECK_CURRENTLY_ON(BrowserThread::UI); return default_zoom_level_; } @@ -341,7 +340,7 @@ } double HostZoomMapImpl::GetZoomLevelForWebContents( - WebContentsImpl* web_contents_impl) const { + WebContentsImpl* web_contents_impl) { DCHECK_CURRENTLY_ON(BrowserThread::UI); int render_process_id = web_contents_impl->GetRenderViewHost()->GetProcess()->GetID(); @@ -432,7 +431,7 @@ } bool HostZoomMapImpl::UsesTemporaryZoomLevel(int render_process_id, - int render_view_id) const { + int render_view_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); RenderViewKey key(render_process_id, render_view_id); return base::ContainsKey(temporary_zoom_levels_, key); @@ -469,7 +468,7 @@ double HostZoomMapImpl::GetZoomLevelForView(const GURL& url, int render_process_id, - int render_view_id) const { + int render_view_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); RenderViewKey key(render_process_id, render_view_id);
diff --git a/content/browser/host_zoom_map_impl.h b/content/browser/host_zoom_map_impl.h index 3bf890a6d..b5df85c 100644 --- a/content/browser/host_zoom_map_impl.h +++ b/content/browser/host_zoom_map_impl.h
@@ -32,11 +32,11 @@ int render_process_id, int render_view_id) override; void CopyFrom(HostZoomMap* copy) override; double GetZoomLevelForHostAndScheme(const std::string& scheme, - const std::string& host) const override; + const std::string& host) override; // TODO(wjmaclean) Should we use a GURL here? crbug.com/384486 bool HasZoomLevel(const std::string& scheme, - const std::string& host) const override; - ZoomLevelVector GetAllZoomLevels() const override; + const std::string& host) override; + ZoomLevelVector GetAllZoomLevels() override; void SetZoomLevelForHost(const std::string& host, double level) override; void InitializeZoomLevelForHost(const std::string& host, double level, @@ -45,21 +45,21 @@ const std::string& host, double level) override; bool UsesTemporaryZoomLevel(int render_process_id, - int render_view_id) const override; + int render_view_id) override; void SetTemporaryZoomLevel(int render_process_id, int render_view_id, double level) override; void ClearZoomLevels(base::Time delete_begin, base::Time delete_end) override; void ClearTemporaryZoomLevel(int render_process_id, int render_view_id) override; - double GetDefaultZoomLevel() const override; + double GetDefaultZoomLevel() override; void SetDefaultZoomLevel(double level) override; std::unique_ptr<Subscription> AddZoomLevelChangedCallback( const ZoomLevelChangedCallback& callback) override; // Returns the current zoom level for the specified WebContents. This may // be a temporary zoom level, depending on UsesTemporaryZoomLevel(). - double GetZoomLevelForWebContents(WebContentsImpl* web_contents_impl) const; + double GetZoomLevelForWebContents(WebContentsImpl* web_contents_impl); bool PageScaleFactorIsOneForWebContents( WebContentsImpl* web_contents_impl) const; @@ -87,7 +87,7 @@ // scheme+host-keyed. double GetZoomLevelForView(const GURL& url, int render_process_id, - int render_view_id) const; + int render_view_id); void SendErrorPageZoomLevelRefresh();
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc index 4af78754..635be69 100644 --- a/content/browser/indexed_db/indexed_db_context_impl.cc +++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -431,7 +431,7 @@ } base::FilePath IndexedDBContextImpl::GetFilePathForTesting( - const Origin& origin) const { + const Origin& origin) { return GetLevelDBPath(origin); } @@ -626,7 +626,7 @@ return origin_set_.get(); } -base::SequencedTaskRunner* IndexedDBContextImpl::TaskRunner() const { +base::SequencedTaskRunner* IndexedDBContextImpl::TaskRunner() { DCHECK(task_runner_.get()); return task_runner_.get(); }
diff --git a/content/browser/indexed_db/indexed_db_context_impl.h b/content/browser/indexed_db/indexed_db_context_impl.h index 0a6117b..cac327b 100644 --- a/content/browser/indexed_db/indexed_db_context_impl.h +++ b/content/browser/indexed_db/indexed_db_context_impl.h
@@ -86,13 +86,12 @@ int64_t GetOriginDiskUsage(const url::Origin& origin); // IndexedDBContext implementation: - base::SequencedTaskRunner* TaskRunner() const override; + base::SequencedTaskRunner* TaskRunner() override; std::vector<StorageUsageInfo> GetAllOriginsInfo() override; void DeleteForOrigin(const url::Origin& origin) override; void CopyOriginData(const url::Origin& origin, IndexedDBContext* dest_context) override; - base::FilePath GetFilePathForTesting( - const url::Origin& origin) const override; + base::FilePath GetFilePathForTesting(const url::Origin& origin) override; void ResetCachesForTesting() override; void SetForceKeepSessionState() override;
diff --git a/content/browser/memory/swap_metrics_driver_impl.cc b/content/browser/memory/swap_metrics_driver_impl.cc index f16e904..849dd38c 100644 --- a/content/browser/memory/swap_metrics_driver_impl.cc +++ b/content/browser/memory/swap_metrics_driver_impl.cc
@@ -38,7 +38,7 @@ return result; } -bool SwapMetricsDriverImpl::IsRunning() const { +bool SwapMetricsDriverImpl::IsRunning() { return timer_.IsRunning(); }
diff --git a/content/browser/memory/swap_metrics_driver_impl.h b/content/browser/memory/swap_metrics_driver_impl.h index 8339110c..b9c406a 100644 --- a/content/browser/memory/swap_metrics_driver_impl.h +++ b/content/browser/memory/swap_metrics_driver_impl.h
@@ -24,7 +24,7 @@ // SwapMetricsDriver SwapMetricsDriver::SwapMetricsUpdateResult InitializeMetrics() override; - bool IsRunning() const override; + bool IsRunning() override; SwapMetricsDriver::SwapMetricsUpdateResult Start() override; void Stop() override; SwapMetricsDriver::SwapMetricsUpdateResult UpdateMetrics() override;
diff --git a/content/browser/network_service_browsertest.cc b/content/browser/network_service_browsertest.cc index 1693e74..076a684a 100644 --- a/content/browser/network_service_browsertest.cc +++ b/content/browser/network_service_browsertest.cc
@@ -105,7 +105,7 @@ TestWebUIDataSource() {} ~TestWebUIDataSource() override {} - std::string GetSource() const override { return "webui"; } + std::string GetSource() override { return "webui"; } void StartDataRequest( const std::string& path, @@ -117,7 +117,7 @@ callback.Run(response.get()); } - std::string GetMimeType(const std::string& path) const override { + std::string GetMimeType(const std::string& path) override { return "text/html"; }
diff --git a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc index b2603154..92fbc9c6 100644 --- a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc +++ b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
@@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "build/build_config.h" #include "content/common/media/media_player_delegate_messages.h" #include "content/public/browser/overlay_window.h" #include "content/public/browser/web_contents_delegate.h" @@ -124,7 +125,14 @@ PictureInPictureServiceImpl* service_impl_; }; -TEST_F(PictureInPictureServiceImplTest, EnterPictureInPicture) { +// Flaky on Android. https://crbug.com/970866 +#if defined(OS_ANDROID) +#define MAYBE_EnterPictureInPicture DISABLED_EnterPictureInPicture +#else +#define MAYBE_EnterPictureInPicture EnterPictureInPicture +#endif + +TEST_F(PictureInPictureServiceImplTest, MAYBE_EnterPictureInPicture) { const int kPlayerVideoOnlyId = 30; DummyPictureInPictureSessionObserver observer;
diff --git a/content/browser/presentation/presentation_service_impl.cc b/content/browser/presentation/presentation_service_impl.cc index d9dc94b..e4c81a6ce 100644 --- a/content/browser/presentation/presentation_service_impl.cc +++ b/content/browser/presentation/presentation_service_impl.cc
@@ -514,7 +514,7 @@ ~ScreenAvailabilityListenerImpl() = default; GURL PresentationServiceImpl::ScreenAvailabilityListenerImpl:: - GetAvailabilityUrl() const { + GetAvailabilityUrl() { return availability_url_; }
diff --git a/content/browser/presentation/presentation_service_impl.h b/content/browser/presentation/presentation_service_impl.h index 58356e8..2306ba95 100644 --- a/content/browser/presentation/presentation_service_impl.h +++ b/content/browser/presentation/presentation_service_impl.h
@@ -117,7 +117,7 @@ ~ScreenAvailabilityListenerImpl() override; // PresentationScreenAvailabilityListener implementation. - GURL GetAvailabilityUrl() const override; + GURL GetAvailabilityUrl() override; void OnScreenAvailabilityChanged( blink::mojom::ScreenAvailability availability) override;
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc index 42bdfd8..07a5b71 100644 --- a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc +++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
@@ -6,6 +6,7 @@ #include "base/metrics/metrics_hashes.h" #include "base/test/metrics/histogram_tester.h" +#include "build/build_config.h" #include "components/rappor/public/rappor_utils.h" #include "components/rappor/test_rappor_service.h" #include "components/ukm/test_ukm_recorder.h" @@ -212,7 +213,16 @@ "Event.Latency.ScrollBegin.Wheel.GpuSwap2", 0, 1); } -TEST_F(RenderWidgetHostLatencyTrackerTest, TestWheelToFirstScrollHistograms) { +// Flaky on Android. https://crbug.com/970841 +#if defined(OS_ANDROID) +#define MAYBE_TestWheelToFirstScrollHistograms \ + DISABLED_TestWheelToFirstScrollHistograms +#else +#define MAYBE_TestWheelToFirstScrollHistograms TestWheelToFirstScrollHistograms +#endif + +TEST_F(RenderWidgetHostLatencyTrackerTest, + MAYBE_TestWheelToFirstScrollHistograms) { const GURL url(kUrl); size_t total_ukm_entry_count = 0; contents()->NavigateAndCommit(url); @@ -318,7 +328,14 @@ } } -TEST_F(RenderWidgetHostLatencyTrackerTest, TestWheelToScrollHistograms) { +// Flaky on Android. https://crbug.com/970841 +#if defined(OS_ANDROID) +#define MAYBE_TestWheelToScrollHistograms DISABLED_TestWheelToScrollHistograms +#else +#define MAYBE_TestWheelToScrollHistograms TestWheelToScrollHistograms +#endif + +TEST_F(RenderWidgetHostLatencyTrackerTest, MAYBE_TestWheelToScrollHistograms) { const GURL url(kUrl); size_t total_ukm_entry_count = 0; contents()->NavigateAndCommit(url); @@ -424,7 +441,16 @@ } } -TEST_F(RenderWidgetHostLatencyTrackerTest, TestInertialToScrollHistograms) { +// Flaky on Android. https://crbug.com/970841 +#if defined(OS_ANDROID) +#define MAYBE_TestInertialToScrollHistograms \ + DISABLED_TestInertialToScrollHistograms +#else +#define MAYBE_TestInertialToScrollHistograms TestInertialToScrollHistograms +#endif + +TEST_F(RenderWidgetHostLatencyTrackerTest, + MAYBE_TestInertialToScrollHistograms) { const GURL url(kUrl); contents()->NavigateAndCommit(url); for (bool rendering_on_main : {false, true}) { @@ -475,7 +501,16 @@ } } -TEST_F(RenderWidgetHostLatencyTrackerTest, TestTouchToFirstScrollHistograms) { +// Flaky on Android. https://crbug.com/970841 +#if defined(OS_ANDROID) +#define MAYBE_TestTouchToFirstScrollHistograms \ + DISABLED_TestTouchToFirstScrollHistograms +#else +#define MAYBE_TestTouchToFirstScrollHistograms TestTouchToFirstScrollHistograms +#endif + +TEST_F(RenderWidgetHostLatencyTrackerTest, + MAYBE_TestTouchToFirstScrollHistograms) { const GURL url(kUrl); contents()->NavigateAndCommit(url); size_t total_ukm_entry_count = 0; @@ -585,7 +620,14 @@ } } -TEST_F(RenderWidgetHostLatencyTrackerTest, TestTouchToScrollHistograms) { +// Flaky on Android. https://crbug.com/970841 +#if defined(OS_ANDROID) +#define MAYBE_TestTouchToScrollHistograms DISABLED_TestTouchToScrollHistograms +#else +#define MAYBE_TestTouchToScrollHistograms TestTouchToScrollHistograms +#endif + +TEST_F(RenderWidgetHostLatencyTrackerTest, MAYBE_TestTouchToScrollHistograms) { const GURL url(kUrl); contents()->NavigateAndCommit(url); size_t total_ukm_entry_count = 0; @@ -692,7 +734,14 @@ } } -TEST_F(RenderWidgetHostLatencyTrackerTest, ScrollbarEndToEndHistograms) { +// Flaky on Android. https://crbug.com/970841 +#if defined(OS_ANDROID) +#define MAYBE_ScrollbarEndToEndHistograms DISABLED_ScrollbarEndToEndHistograms +#else +#define MAYBE_ScrollbarEndToEndHistograms ScrollbarEndToEndHistograms +#endif + +TEST_F(RenderWidgetHostLatencyTrackerTest, MAYBE_ScrollbarEndToEndHistograms) { // For all combinations of ScrollBegin/ScrollUpdate main/impl rendering, // ensure that the LatencyTracker logs the correct set of histograms. const GURL url(kUrl);
diff --git a/content/browser/renderer_host/render_frame_metadata_provider_impl.cc b/content/browser/renderer_host/render_frame_metadata_provider_impl.cc index 760bb72..5c3dbee 100644 --- a/content/browser/renderer_host/render_frame_metadata_provider_impl.cc +++ b/content/browser/renderer_host/render_frame_metadata_provider_impl.cc
@@ -53,7 +53,7 @@ } const cc::RenderFrameMetadata& -RenderFrameMetadataProviderImpl::LastRenderFrameMetadata() const { +RenderFrameMetadataProviderImpl::LastRenderFrameMetadata() { return last_render_frame_metadata_; }
diff --git a/content/browser/renderer_host/render_frame_metadata_provider_impl.h b/content/browser/renderer_host/render_frame_metadata_provider_impl.h index 2ec032d..5e0ddf9f 100644 --- a/content/browser/renderer_host/render_frame_metadata_provider_impl.h +++ b/content/browser/renderer_host/render_frame_metadata_provider_impl.h
@@ -42,7 +42,7 @@ // submissions. void ReportAllFrameSubmissionsForTesting(bool enabled) override; - const cc::RenderFrameMetadata& LastRenderFrameMetadata() const override; + const cc::RenderFrameMetadata& LastRenderFrameMetadata() override; private: friend class FakeRenderWidgetHostViewAura;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index bad90d7..50d6f67 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1548,7 +1548,7 @@ return bluetooth_connected_device_count_ > 0; } -bool WebContentsImpl::IsConnectedToSerialPort() const { +bool WebContentsImpl::IsConnectedToSerialPort() { return serial_active_frame_count_ > 0; }
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index f08208b..415d4a1 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h
@@ -344,7 +344,7 @@ void SetAudioMuted(bool mute) override; bool IsCurrentlyAudible() override; bool IsConnectedToBluetoothDevice() override; - bool IsConnectedToSerialPort() const override; + bool IsConnectedToSerialPort() override; bool HasPictureInPictureVideo() override; bool IsCrashed() override; void SetIsCrashed(base::TerminationStatus status, int error_code) override;
diff --git a/content/browser/webauth/webauth_browsertest.cc b/content/browser/webauth/webauth_browsertest.cc index f45c8ec..d68f328 100644 --- a/content/browser/webauth/webauth_browsertest.cc +++ b/content/browser/webauth/webauth_browsertest.cc
@@ -95,7 +95,7 @@ "webauth: SecurityError: 'rp.icon' should be a secure URL"; constexpr char kAbortErrorMessage[] = - "webauth: AbortError: The user aborted a request."; + "webauth: AbortError: Request has been aborted."; // Templates to be used with base::ReplaceStringPlaceholders. Can be // modified to include up to 9 replacements. The default values for
diff --git a/content/browser/webui/shared_resources_data_source.cc b/content/browser/webui/shared_resources_data_source.cc index cce9341..3030e949 100644 --- a/content/browser/webui/shared_resources_data_source.cc +++ b/content/browser/webui/shared_resources_data_source.cc
@@ -273,7 +273,7 @@ SharedResourcesDataSource::~SharedResourcesDataSource() { } -std::string SharedResourcesDataSource::GetSource() const { +std::string SharedResourcesDataSource::GetSource() { return kChromeUIResourcesHost; } @@ -314,14 +314,13 @@ callback.Run(bytes.get()); } -bool SharedResourcesDataSource::AllowCaching() const { +bool SharedResourcesDataSource::AllowCaching() { // Should not be cached to reflect dynamically-generated contents that may // depend on the current locale. return false; } -std::string SharedResourcesDataSource::GetMimeType( - const std::string& path) const { +std::string SharedResourcesDataSource::GetMimeType(const std::string& path) { if (path.empty()) return "text/html"; @@ -361,13 +360,12 @@ return "text/plain"; } -bool SharedResourcesDataSource::ShouldServeMimeTypeAsContentTypeHeader() const { +bool SharedResourcesDataSource::ShouldServeMimeTypeAsContentTypeHeader() { return true; } scoped_refptr<base::SingleThreadTaskRunner> -SharedResourcesDataSource::TaskRunnerForRequestPath( - const std::string& path) const { +SharedResourcesDataSource::TaskRunnerForRequestPath(const std::string& path) { // Since WebContentsGetter can only be run on the UI thread, always return // a task runner if we need to choose between Polymer resources based on the // WebContents that is requesting the resource. @@ -388,9 +386,8 @@ return nullptr; } -std::string -SharedResourcesDataSource::GetAccessControlAllowOriginForOrigin( - const std::string& origin) const { +std::string SharedResourcesDataSource::GetAccessControlAllowOriginForOrigin( + const std::string& origin) { // For now we give access only for "chrome://*" origins. // According to CORS spec, Access-Control-Allow-Origin header doesn't support // wildcards, so we need to set its value explicitly by passing the |origin| @@ -404,7 +401,7 @@ return origin; } -bool SharedResourcesDataSource::IsGzipped(const std::string& path) const { +bool SharedResourcesDataSource::IsGzipped(const std::string& path) { return GetContentClient()->IsDataResourceGzipped(GetIdrForPath(path)); }
diff --git a/content/browser/webui/shared_resources_data_source.h b/content/browser/webui/shared_resources_data_source.h index 4e114cf..36b6a63 100644 --- a/content/browser/webui/shared_resources_data_source.h +++ b/content/browser/webui/shared_resources_data_source.h
@@ -20,19 +20,19 @@ SharedResourcesDataSource(); // URLDataSource implementation. - std::string GetSource() const override; + std::string GetSource() override; void StartDataRequest( const std::string& path, const ResourceRequestInfo::WebContentsGetter& wc_getter, const URLDataSource::GotDataCallback& callback) override; - bool AllowCaching() const override; - std::string GetMimeType(const std::string& path) const override; - bool ShouldServeMimeTypeAsContentTypeHeader() const override; + bool AllowCaching() override; + std::string GetMimeType(const std::string& path) override; + bool ShouldServeMimeTypeAsContentTypeHeader() override; scoped_refptr<base::SingleThreadTaskRunner> TaskRunnerForRequestPath( - const std::string& path) const override; + const std::string& path) override; std::string GetAccessControlAllowOriginForOrigin( - const std::string& origin) const override; - bool IsGzipped(const std::string& path) const override; + const std::string& origin) override; + bool IsGzipped(const std::string& path) override; #if defined(OS_CHROMEOS) void DisablePolymer2ForHost(const std::string& host) override; #endif // defined (OS_CHROMEOS)
diff --git a/content/browser/webui/web_ui_data_source_impl.cc b/content/browser/webui/web_ui_data_source_impl.cc index 789bb881..84a12355 100644 --- a/content/browser/webui/web_ui_data_source_impl.cc +++ b/content/browser/webui/web_ui_data_source_impl.cc
@@ -63,8 +63,8 @@ ~InternalDataSource() override {} // URLDataSource implementation. - std::string GetSource() const override { return parent_->GetSource(); } - std::string GetMimeType(const std::string& path) const override { + std::string GetSource() override { return parent_->GetSource(); } + std::string GetMimeType(const std::string& path) override { return parent_->GetMimeType(path); } void StartDataRequest( @@ -73,33 +73,31 @@ const URLDataSource::GotDataCallback& callback) override { return parent_->StartDataRequest(path, wc_getter, callback); } - bool ShouldReplaceExistingSource() const override { + bool ShouldReplaceExistingSource() override { return parent_->replace_existing_source_; } - bool AllowCaching() const override { return false; } - bool ShouldAddContentSecurityPolicy() const override { - return parent_->add_csp_; - } - std::string GetContentSecurityPolicyScriptSrc() const override { + bool AllowCaching() override { return false; } + bool ShouldAddContentSecurityPolicy() override { return parent_->add_csp_; } + std::string GetContentSecurityPolicyScriptSrc() override { if (parent_->script_src_set_) return parent_->script_src_; return URLDataSource::GetContentSecurityPolicyScriptSrc(); } - std::string GetContentSecurityPolicyObjectSrc() const override { + std::string GetContentSecurityPolicyObjectSrc() override { if (parent_->object_src_set_) return parent_->object_src_; return URLDataSource::GetContentSecurityPolicyObjectSrc(); } - std::string GetContentSecurityPolicyChildSrc() const override { + std::string GetContentSecurityPolicyChildSrc() override { if (parent_->frame_src_set_) return parent_->frame_src_; return URLDataSource::GetContentSecurityPolicyChildSrc(); } - bool ShouldDenyXFrameOptions() const override { + bool ShouldDenyXFrameOptions() override { return parent_->deny_xframe_options_; } - bool ShouldServeMimeTypeAsContentTypeHeader() const override { return true; } - bool IsGzipped(const std::string& path) const override { + bool ShouldServeMimeTypeAsContentTypeHeader() override { return true; } + bool IsGzipped(const std::string& path) override { return parent_->IsGzipped(path); } @@ -235,7 +233,7 @@ AddLocalizedStrings(defaults); } -std::string WebUIDataSourceImpl::GetSource() const { +std::string WebUIDataSourceImpl::GetSource() { return source_name_; }
diff --git a/content/browser/webui/web_ui_data_source_impl.h b/content/browser/webui/web_ui_data_source_impl.h index 9de2d81..3d09b66 100644 --- a/content/browser/webui/web_ui_data_source_impl.h +++ b/content/browser/webui/web_ui_data_source_impl.h
@@ -50,7 +50,7 @@ void OverrideContentSecurityPolicyObjectSrc(const std::string& data) override; void OverrideContentSecurityPolicyChildSrc(const std::string& data) override; void DisableDenyXFrameOptions() override; - std::string GetSource() const override; + std::string GetSource() override; // URLDataSourceImpl: const ui::TemplateReplacements* GetReplacements() const override;
diff --git a/content/browser/webui/web_ui_impl.cc b/content/browser/webui/web_ui_impl.cc index 8ebddd2e..a6d18fa 100644 --- a/content/browser/webui/web_ui_impl.cc +++ b/content/browser/webui/web_ui_impl.cc
@@ -150,15 +150,15 @@ DisallowJavascriptOnAllHandlers(); } -WebContents* WebUIImpl::GetWebContents() const { +WebContents* WebUIImpl::GetWebContents() { return web_contents_; } -float WebUIImpl::GetDeviceScaleFactor() const { +float WebUIImpl::GetDeviceScaleFactor() { return GetScaleFactorForView(web_contents_->GetRenderWidgetHostView()); } -const base::string16& WebUIImpl::GetOverriddenTitle() const { +const base::string16& WebUIImpl::GetOverriddenTitle() { return overridden_title_; } @@ -166,7 +166,7 @@ overridden_title_ = title; } -int WebUIImpl::GetBindings() const { +int WebUIImpl::GetBindings() { return bindings_; } @@ -174,7 +174,7 @@ bindings_ = bindings; } -WebUIController* WebUIImpl::GetController() const { +WebUIController* WebUIImpl::GetController() { return controller_.get(); }
diff --git a/content/browser/webui/web_ui_impl.h b/content/browser/webui/web_ui_impl.h index a0e36cec..2bd6dbe 100644 --- a/content/browser/webui/web_ui_impl.h +++ b/content/browser/webui/web_ui_impl.h
@@ -43,13 +43,13 @@ void RenderFrameHostSwappingOut(); // WebUI implementation: - WebContents* GetWebContents() const override; - WebUIController* GetController() const override; + WebContents* GetWebContents() override; + WebUIController* GetController() override; void SetController(std::unique_ptr<WebUIController> controller) override; - float GetDeviceScaleFactor() const override; - const base::string16& GetOverriddenTitle() const override; + float GetDeviceScaleFactor() override; + const base::string16& GetOverriddenTitle() override; void OverrideTitle(const base::string16& title) override; - int GetBindings() const override; + int GetBindings() override; void SetBindings(int bindings) override; void AddMessageHandler(std::unique_ptr<WebUIMessageHandler> handler) override; void RegisterMessageCallback(base::StringPiece message,
diff --git a/content/common/user_agent.cc b/content/common/user_agent.cc index f37818e..65083a0 100644 --- a/content/common/user_agent.cc +++ b/content/common/user_agent.cc
@@ -16,7 +16,7 @@ #if defined(OS_WIN) #include "base/win/windows_version.h" -#elif (defined(OS_POSIX) && !defined(OS_MACOSX)) || defined(OS_FUCHSIA) +#elif defined(OS_POSIX) && !defined(OS_MACOSX) #include <sys/utsname.h> #endif @@ -30,17 +30,16 @@ #endif // defined(OS_ANDROID) std::string GetUserAgentPlatform() { - return -#if defined(OS_WIN) - ""; +#if defined(OS_WIN) || defined(OS_FUCHSIA) + return ""; #elif defined(OS_MACOSX) - "Macintosh; "; + return "Macintosh; "; #elif defined(USE_X11) || defined(USE_OZONE) - "X11; "; // strange, but that's what Firefox uses + return "X11; "; // strange, but that's what Firefox uses #elif defined(OS_ANDROID) - "Linux; "; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - "Unknown; "; + return "Linux; "; +#elif defined(OS_POSIX) + return "Unknown; "; #endif } @@ -86,7 +85,7 @@ #elif defined(OS_ANDROID) std::string android_version_str = base::SysInfo::OperatingSystemVersion(); std::string android_info_str = GetAndroidOSInfo(include_android_build_number); -#elif (defined(OS_POSIX) && !defined(OS_MACOSX)) || defined(OS_FUCHSIA) +#elif defined(OS_POSIX) && !defined(OS_MACOSX) // Should work on any Posix system. struct utsname unixinfo; uname(&unixinfo); @@ -94,42 +93,36 @@ std::string cputype; // special case for biarch systems if (strcmp(unixinfo.machine, "x86_64") == 0 && - sizeof(void*) == sizeof(int32_t)) { // NOLINT + sizeof(void*) == sizeof(int32_t)) { cputype.assign("i686 (x86_64)"); } else { cputype.assign(unixinfo.machine); } #endif - base::StringAppendF( - &os_cpu, + base::StringAppendF(&os_cpu, #if defined(OS_WIN) - "Windows NT %d.%d%s", - os_major_version, - os_minor_version, - architecture_token.c_str() + "Windows NT %d.%d%s", os_major_version, os_minor_version, + architecture_token.c_str() #elif defined(OS_MACOSX) - "Intel Mac OS X %d_%d_%d", - os_major_version, - os_minor_version, - os_bugfix_version + "Intel Mac OS X %d_%d_%d", os_major_version, + os_minor_version, os_bugfix_version #elif defined(OS_CHROMEOS) - "CrOS " - "%s %d.%d.%d", - cputype.c_str(), // e.g. i686 - os_major_version, - os_minor_version, - os_bugfix_version + "CrOS " + "%s %d.%d.%d", + cputype.c_str(), // e.g. i686 + os_major_version, os_minor_version, os_bugfix_version #elif defined(OS_ANDROID) - "Android %s%s", - android_version_str.c_str(), - android_info_str.c_str() -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) + "Android %s%s", android_version_str.c_str(), + android_info_str.c_str() +#elif defined(OS_FUCHSIA) + "Fuchsia" +#elif defined(OS_POSIX) "%s %s", unixinfo.sysname, // e.g. Linux cputype.c_str() // e.g. i686 #endif - ); // NOLINT + ); return os_cpu; }
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/OWNERS b/content/public/android/java/src/org/chromium/content/browser/selection/OWNERS index 4943acc..395fd997 100644 --- a/content/public/android/java/src/org/chromium/content/browser/selection/OWNERS +++ b/content/public/android/java/src/org/chromium/content/browser/selection/OWNERS
@@ -1 +1 @@ -amaralp@chromium.org +ctzsm@chromium.org
diff --git a/content/public/android/junit/src/org/chromium/content/browser/selection/OWNERS b/content/public/android/junit/src/org/chromium/content/browser/selection/OWNERS index 4943acc..395fd997 100644 --- a/content/public/android/junit/src/org/chromium/content/browser/selection/OWNERS +++ b/content/public/android/junit/src/org/chromium/content/browser/selection/OWNERS
@@ -1 +1 @@ -amaralp@chromium.org +ctzsm@chromium.org
diff --git a/content/public/browser/gpu_data_manager.h b/content/public/browser/gpu_data_manager.h index 46dec9d..76a2d18 100644 --- a/content/public/browser/gpu_data_manager.h +++ b/content/public/browser/gpu_data_manager.h
@@ -42,7 +42,7 @@ // This is only called by extensions testing. virtual void BlacklistWebGLForTesting() = 0; - virtual gpu::GPUInfo GetGPUInfo() const = 0; + virtual gpu::GPUInfo GetGPUInfo() = 0; // This indicator might change because we could collect more GPU info or // because the GPU blacklist could be updated. @@ -51,17 +51,17 @@ // Can be called on any thread. // If |reason| is not nullptr and GPU access is blocked, upon return, |reason| // contains a description of the reason why GPU access is blocked. - virtual bool GpuAccessAllowed(std::string* reason) const = 0; + virtual bool GpuAccessAllowed(std::string* reason) = 0; // Requests complete GPU info if it has not already been requested virtual void RequestCompleteGpuInfoIfNeeded() = 0; // Check if basic and context GPU info have been collected. - virtual bool IsEssentialGpuInfoAvailable() const = 0; + virtual bool IsEssentialGpuInfoAvailable() = 0; // Requests that the GPU process report its current video memory usage stats. virtual void RequestVideoMemoryUsageStatsUpdate( - VideoMemoryUsageStatsCallback callback) const = 0; + VideoMemoryUsageStatsCallback callback) = 0; // Registers/unregister |observer|. virtual void AddObserver(GpuDataManagerObserver* observer) = 0; @@ -70,11 +70,11 @@ virtual void DisableHardwareAcceleration() = 0; // Whether a GPU is in use (as opposed to a software renderer). - virtual bool HardwareAccelerationEnabled() const = 0; + virtual bool HardwareAccelerationEnabled() = 0; // Insert switches into gpu process command line: kUseGL, etc. virtual void AppendGpuCommandLine(base::CommandLine* command_line, - GpuProcessKind kind) const = 0; + GpuProcessKind kind) = 0; protected: virtual ~GpuDataManager() {}
diff --git a/content/public/browser/host_zoom_map.h b/content/public/browser/host_zoom_map.h index 50ee4fb..1335258b 100644 --- a/content/public/browser/host_zoom_map.h +++ b/content/public/browser/host_zoom_map.h
@@ -112,19 +112,18 @@ // description for details. // // This may be called on any thread. - virtual double GetZoomLevelForHostAndScheme( - const std::string& scheme, - const std::string& host) const = 0; + virtual double GetZoomLevelForHostAndScheme(const std::string& scheme, + const std::string& host) = 0; // Returns true if the specified |scheme| and/or |host| has a zoom level // currently set. // // This may be called on any thread. virtual bool HasZoomLevel(const std::string& scheme, - const std::string& host) const = 0; + const std::string& host) = 0; // Returns all non-temporary zoom levels. Can be called on any thread. - virtual ZoomLevelVector GetAllZoomLevels() const = 0; + virtual ZoomLevelVector GetAllZoomLevels() = 0; // Here |host| is the host portion of URL, or (in the absence of a host) // the complete spec of the URL. @@ -156,7 +155,7 @@ // Returns whether the view manages its zoom level independently of other // views displaying content from the same host. virtual bool UsesTemporaryZoomLevel(int render_process_id, - int render_view_id) const = 0; + int render_view_id) = 0; // Sets the temporary zoom level that's only valid for the lifetime of this // WebContents. @@ -179,7 +178,7 @@ int render_view_id) = 0; // Get/Set the default zoom level for pages that don't override it. - virtual double GetDefaultZoomLevel() const = 0; + virtual double GetDefaultZoomLevel() = 0; virtual void SetDefaultZoomLevel(double level) = 0; typedef base::Callback<void(const ZoomLevelChange&)> ZoomLevelChangedCallback;
diff --git a/content/public/browser/indexed_db_context.h b/content/public/browser/indexed_db_context.h index 36bc0d3..9a271d1f 100644 --- a/content/public/browser/indexed_db_context.h +++ b/content/public/browser/indexed_db_context.h
@@ -33,7 +33,7 @@ class IndexedDBContext : public base::RefCountedThreadSafe<IndexedDBContext> { public: // Only call the below methods by posting to this TaskRunner. - virtual base::SequencedTaskRunner* TaskRunner() const = 0; + virtual base::SequencedTaskRunner* TaskRunner() = 0; // Methods used in response to QuotaManager requests. virtual std::vector<StorageUsageInfo> GetAllOriginsInfo() = 0; @@ -47,8 +47,7 @@ IndexedDBContext* dest_context) = 0; // Get the file name of the local storage file for the given origin. - virtual base::FilePath GetFilePathForTesting( - const url::Origin& origin) const = 0; + virtual base::FilePath GetFilePathForTesting(const url::Origin& origin) = 0; // Forget the origins/sizes read from disk. virtual void ResetCachesForTesting() = 0;
diff --git a/content/public/browser/navigation_controller.cc b/content/public/browser/navigation_controller.cc index 28abe11..7b11ca8f 100644 --- a/content/public/browser/navigation_controller.cc +++ b/content/public/browser/navigation_controller.cc
@@ -25,6 +25,7 @@ should_clear_history_list(false), started_from_context_menu(false), navigation_ui_data(nullptr), + from_download_cross_origin_redirect(false), was_activated(WasActivatedOption::kUnknown), reload_type(ReloadType::NONE) {}
diff --git a/content/public/browser/navigation_controller.h b/content/public/browser/navigation_controller.h index 5211573a..da0c06a9 100644 --- a/content/public/browser/navigation_controller.h +++ b/content/public/browser/navigation_controller.h
@@ -201,6 +201,10 @@ // ContentBrowserClient::GetNavigationUIData. std::unique_ptr<NavigationUIData> navigation_ui_data; + // Whether this navigation was triggered by a x-origin redirect following a + // prior (most likely <a download>) download attempt. + bool from_download_cross_origin_redirect; + // Time at which the input leading to this navigation occurred. This field // is set for links clicked by the user; the embedder is recommended to set // it for navigations it initiates.
diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h index bb0a5bc..7436eb4 100644 --- a/content/public/browser/navigation_handle.h +++ b/content/public/browser/navigation_handle.h
@@ -343,6 +343,10 @@ // Returns whether this navigation is currently deferred. virtual bool IsDeferredForTesting() = 0; + + // Whether this navigation was triggered by a x-origin redirect following a + // prior (most likely <a download>) download attempt. + virtual bool FromDownloadCrossOriginRedirect() = 0; }; } // namespace content
diff --git a/content/public/browser/presentation_screen_availability_listener.h b/content/public/browser/presentation_screen_availability_listener.h index 09ab473..eaf51967 100644 --- a/content/public/browser/presentation_screen_availability_listener.h +++ b/content/public/browser/presentation_screen_availability_listener.h
@@ -23,7 +23,7 @@ // Returns the screen availability URL associated with this listener. // Empty string means this object is listening for screen availability // for "1-UA" mode, i.e. offscreen tab rendering. - virtual GURL GetAvailabilityUrl() const = 0; + virtual GURL GetAvailabilityUrl() = 0; // Called when screen availability for the associated Presentation URL has // changed to |availability|.
diff --git a/content/public/browser/push_messaging_service.h b/content/public/browser/push_messaging_service.h index 7b2a07a..9f361a6 100644 --- a/content/public/browser/push_messaging_service.h +++ b/content/public/browser/push_messaging_service.h
@@ -51,7 +51,7 @@ // Returns the absolute URL to the endpoint of the push service where messages // should be posted to. Should return an endpoint compatible with the Web Push // Protocol when |standard_protocol| is true. - virtual GURL GetEndpoint(bool standard_protocol) const = 0; + virtual GURL GetEndpoint(bool standard_protocol) = 0; // Subscribe the given |options.sender_info| with the push messaging service // in a document context. The frame is known and a permission UI may be
diff --git a/content/public/browser/render_frame_metadata_provider.h b/content/public/browser/render_frame_metadata_provider.h index 3072f9c..590bdf2 100644 --- a/content/public/browser/render_frame_metadata_provider.h +++ b/content/public/browser/render_frame_metadata_provider.h
@@ -52,7 +52,7 @@ // submissions. virtual void ReportAllFrameSubmissionsForTesting(bool enabled) = 0; - virtual const cc::RenderFrameMetadata& LastRenderFrameMetadata() const = 0; + virtual const cc::RenderFrameMetadata& LastRenderFrameMetadata() = 0; private: DISALLOW_COPY_AND_ASSIGN(RenderFrameMetadataProvider);
diff --git a/content/public/browser/ssl_host_state_delegate.h b/content/public/browser/ssl_host_state_delegate.h index 9d8f1c2..de25f85 100644 --- a/content/public/browser/ssl_host_state_delegate.h +++ b/content/public/browser/ssl_host_state_delegate.h
@@ -66,10 +66,9 @@ // Returns whether the specified host ran insecure content of the given // |content_type|. - virtual bool DidHostRunInsecureContent( - const std::string& host, - int child_id, - InsecureContentType content_type) const = 0; + virtual bool DidHostRunInsecureContent(const std::string& host, + int child_id, + InsecureContentType content_type) = 0; // Revokes all SSL certificate error allow exceptions made by the user for // |host|. @@ -79,7 +78,7 @@ // |host|. This does not mean that *all* certificate errors are allowed, just // that there exists an exception. To see if a particular certificate and // error combination exception is allowed, use QueryPolicy(). - virtual bool HasAllowException(const std::string& host) const = 0; + virtual bool HasAllowException(const std::string& host) = 0; protected: virtual ~SSLHostStateDelegate() {}
diff --git a/content/public/browser/swap_metrics_driver.h b/content/public/browser/swap_metrics_driver.h index 58684fa2..382f8fdc 100644 --- a/content/public/browser/swap_metrics_driver.h +++ b/content/public/browser/swap_metrics_driver.h
@@ -84,7 +84,7 @@ // This method must be called from the same sequence that Start() is called // on. If Start() has not yet been called, the subsequent call to Start() must // be from the same sequence. - virtual bool IsRunning() const = 0; + virtual bool IsRunning() = 0; // Starts computing swap metrics every at the interval specified in the // constructor, which must be > 0. If an error occurs while initializing the
diff --git a/content/public/browser/url_data_source.cc b/content/public/browser/url_data_source.cc index 31edfa0..14a367a 100644 --- a/content/public/browser/url_data_source.cc +++ b/content/public/browser/url_data_source.cc
@@ -53,64 +53,64 @@ } scoped_refptr<base::SingleThreadTaskRunner> -URLDataSource::TaskRunnerForRequestPath(const std::string& path) const { +URLDataSource::TaskRunnerForRequestPath(const std::string& path) { return base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}); } -bool URLDataSource::ShouldReplaceExistingSource() const { +bool URLDataSource::ShouldReplaceExistingSource() { return true; } -bool URLDataSource::AllowCaching() const { +bool URLDataSource::AllowCaching() { return true; } -bool URLDataSource::ShouldAddContentSecurityPolicy() const { +bool URLDataSource::ShouldAddContentSecurityPolicy() { return true; } -std::string URLDataSource::GetContentSecurityPolicyScriptSrc() const { +std::string URLDataSource::GetContentSecurityPolicyScriptSrc() { // Note: Do not add 'unsafe-eval' here. Instead override CSP for the // specific pages that need it, see context http://crbug.com/525224. return "script-src chrome://resources 'self';"; } -std::string URLDataSource::GetContentSecurityPolicyObjectSrc() const { +std::string URLDataSource::GetContentSecurityPolicyObjectSrc() { return "object-src 'none';"; } -std::string URLDataSource::GetContentSecurityPolicyChildSrc() const { +std::string URLDataSource::GetContentSecurityPolicyChildSrc() { return "child-src 'none';"; } -std::string URLDataSource::GetContentSecurityPolicyStyleSrc() const { +std::string URLDataSource::GetContentSecurityPolicyStyleSrc() { return std::string(); } -std::string URLDataSource::GetContentSecurityPolicyImgSrc() const { +std::string URLDataSource::GetContentSecurityPolicyImgSrc() { return std::string(); } -bool URLDataSource::ShouldDenyXFrameOptions() const { +bool URLDataSource::ShouldDenyXFrameOptions() { return true; } bool URLDataSource::ShouldServiceRequest(const GURL& url, ResourceContext* resource_context, - int render_process_id) const { + int render_process_id) { return url.SchemeIs(kChromeDevToolsScheme) || url.SchemeIs(kChromeUIScheme); } -bool URLDataSource::ShouldServeMimeTypeAsContentTypeHeader() const { +bool URLDataSource::ShouldServeMimeTypeAsContentTypeHeader() { return false; } std::string URLDataSource::GetAccessControlAllowOriginForOrigin( - const std::string& origin) const { + const std::string& origin) { return std::string(); } -bool URLDataSource::IsGzipped(const std::string& path) const { +bool URLDataSource::IsGzipped(const std::string& path) { return false; }
diff --git a/content/public/browser/url_data_source.h b/content/public/browser/url_data_source.h index 4fc97fb5..9ccdddfa 100644 --- a/content/public/browser/url_data_source.h +++ b/content/public/browser/url_data_source.h
@@ -52,7 +52,7 @@ // identifier, the suffix "://" must be added to the return value, eg. for a // URLDataSource which would display resources with URLs on the form // your-scheme://anything , GetSource() must return "your-scheme://". - virtual std::string GetSource() const = 0; + virtual std::string GetSource() = 0; // Used by StartDataRequest so that the child class can return the data when // it's available. @@ -78,7 +78,7 @@ // Return the mimetype that should be sent with this response, or empty // string to specify no mime type. - virtual std::string GetMimeType(const std::string& path) const = 0; + virtual std::string GetMimeType(const std::string& path) = 0; // Returns the TaskRunner on which the delegate wishes to have // StartDataRequest called to handle the request for |path|. The default @@ -89,7 +89,7 @@ // requests more rapidly when there is a large amount of UI thread contention. // Or the delegate can return a specific thread's TaskRunner if they wish. virtual scoped_refptr<base::SingleThreadTaskRunner> TaskRunnerForRequestPath( - const std::string& path) const; + const std::string& path); // Returns true if the URLDataSource should replace an existing URLDataSource // with the same name that has already been registered. The default is true. @@ -97,10 +97,10 @@ // WARNING: this is invoked on the IO thread. // // TODO: nuke this and convert all callers to not replace. - virtual bool ShouldReplaceExistingSource() const; + virtual bool ShouldReplaceExistingSource(); // Returns true if responses from this URLDataSource can be cached. - virtual bool AllowCaching() const; + virtual bool AllowCaching(); // If you are overriding the following two methods, then you have a bug. // It is not acceptable to disable content-security-policy on chrome:// pages @@ -109,30 +109,30 @@ // compliant with the policy. This typically involves ensuring that all script // is delivered through the data manager backend. Do not disable CSP on your // page without first contacting the chrome security team. - virtual bool ShouldAddContentSecurityPolicy() const; + virtual bool ShouldAddContentSecurityPolicy(); // For pre-existing code, enabling CSP with relaxed script-src attributes // may be marginally better than disabling CSP outright. // Do not override this method without first contacting the chrome security // team. // By default, "script-src chrome://resources 'self';" is added to CSP. // Override to change this. - virtual std::string GetContentSecurityPolicyScriptSrc() const; + virtual std::string GetContentSecurityPolicyScriptSrc(); // It is OK to override the following methods to a custom CSP directive // thereby slightly reducing the protection applied to the page. // By default, "object-src 'none';" is added to CSP. Override to change this. - virtual std::string GetContentSecurityPolicyObjectSrc() const; + virtual std::string GetContentSecurityPolicyObjectSrc(); // By default, "child-src 'none';" is added to CSP. Override to change this. - virtual std::string GetContentSecurityPolicyChildSrc() const; + virtual std::string GetContentSecurityPolicyChildSrc(); // By default empty. Override to change this. - virtual std::string GetContentSecurityPolicyStyleSrc() const; + virtual std::string GetContentSecurityPolicyStyleSrc(); // By default empty. Override to change this. - virtual std::string GetContentSecurityPolicyImgSrc() const; + virtual std::string GetContentSecurityPolicyImgSrc(); // By default, the "X-Frame-Options: DENY" header is sent. To stop this from // happening, return false. It is OK to return false as needed. - virtual bool ShouldDenyXFrameOptions() const; + virtual bool ShouldDenyXFrameOptions(); // By default, only chrome: and devtools: requests are allowed. // Override in specific WebUI data sources to enable for additional schemes or @@ -141,14 +141,14 @@ // WebUI scheme support for an embedder. virtual bool ShouldServiceRequest(const GURL& url, ResourceContext* resource_context, - int render_process_id) const; + int render_process_id); // By default, Content-Type: header is not sent along with the response. // To start sending mime type returned by GetMimeType in HTTP headers, // return true. It is useful when tunneling response served from this data // source programmatically. Or when AppCache is enabled for this source as it // is for devtools. - virtual bool ShouldServeMimeTypeAsContentTypeHeader() const; + virtual bool ShouldServeMimeTypeAsContentTypeHeader(); // This method is called when the request contains "Origin:" header. The value // of the header is passed in |origin| parameter. If the returned value is not @@ -157,10 +157,10 @@ // |origin|, or "*", or "none", or empty string. // Default implementation returns an empty string. virtual std::string GetAccessControlAllowOriginForOrigin( - const std::string& origin) const; + const std::string& origin); // Whether |path| is gzipped (and should be transmitted gzipped). - virtual bool IsGzipped(const std::string& path) const; + virtual bool IsGzipped(const std::string& path); // Called on the UI thread. For the shared resource, disables using Polymer 2 // for requests from |host|, even if WebUIPolymer2 is enabled. Assumes this
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index 6cf2ca2..dd69a8ac 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h
@@ -476,7 +476,7 @@ // Indicates whether any frame in the WebContents is connected to a serial // port. - virtual bool IsConnectedToSerialPort() const = 0; + virtual bool IsConnectedToSerialPort() = 0; // Indicates whether a video is in Picture-in-Picture for |this|. virtual bool HasPictureInPictureVideo() = 0;
diff --git a/content/public/browser/web_ui.h b/content/public/browser/web_ui.h index 1c46a1a..759056d 100644 --- a/content/public/browser/web_ui.h +++ b/content/public/browser/web_ui.h
@@ -48,25 +48,25 @@ virtual ~WebUI() {} - virtual WebContents* GetWebContents() const = 0; + virtual WebContents* GetWebContents() = 0; - virtual WebUIController* GetController() const = 0; + virtual WebUIController* GetController() = 0; virtual void SetController(std::unique_ptr<WebUIController> controller) = 0; // Returns the device scale factor of the monitor that the renderer is on. // Whenever possible, WebUI should push resources with this scale factor to // Javascript. - virtual float GetDeviceScaleFactor() const = 0; + virtual float GetDeviceScaleFactor() = 0; // Gets a custom tab title provided by the Web UI. If there is no title // override, the string will be empty which should trigger the default title // behavior for the tab. - virtual const base::string16& GetOverriddenTitle() const = 0; + virtual const base::string16& GetOverriddenTitle() = 0; virtual void OverrideTitle(const base::string16& title) = 0; // Allows a controller to override the BindingsPolicy that should be enabled // for this page. - virtual int GetBindings() const = 0; + virtual int GetBindings() = 0; virtual void SetBindings(int bindings) = 0; virtual void AddMessageHandler(
diff --git a/content/public/browser/web_ui_data_source.h b/content/public/browser/web_ui_data_source.h index 7a1973a..d200317 100644 --- a/content/public/browser/web_ui_data_source.h +++ b/content/public/browser/web_ui_data_source.h
@@ -119,7 +119,7 @@ virtual void DisableDenyXFrameOptions() = 0; // The |source_name| this WebUIDataSource was created with. - virtual std::string GetSource() const = 0; + virtual std::string GetSource() = 0; }; } // namespace content
diff --git a/content/public/test/mock_navigation_handle.h b/content/public/test/mock_navigation_handle.h index e02af24c..554a7c4 100644 --- a/content/public/test/mock_navigation_handle.h +++ b/content/public/test/mock_navigation_handle.h
@@ -97,6 +97,7 @@ MOCK_METHOD0(IsDeferredForTesting, bool()); MOCK_METHOD1(RegisterSubresourceOverride, void(mojom::TransferrableURLLoaderPtr)); + MOCK_METHOD0(FromDownloadCrossOriginRedirect, bool()); MOCK_METHOD0(IsSameProcess, bool()); MOCK_METHOD0(GetNavigationEntryOffset, int());
diff --git a/content/public/test/test_web_ui.cc b/content/public/test/test_web_ui.cc index a591c5a..5184e71 100644 --- a/content/public/test/test_web_ui.cc +++ b/content/public/test/test_web_ui.cc
@@ -40,11 +40,11 @@ callback.Run(args); } -WebContents* TestWebUI::GetWebContents() const { +WebContents* TestWebUI::GetWebContents() { return web_contents_; } -WebUIController* TestWebUI::GetController() const { +WebUIController* TestWebUI::GetController() { return controller_.get(); } @@ -52,15 +52,15 @@ controller_ = std::move(controller); } -float TestWebUI::GetDeviceScaleFactor() const { +float TestWebUI::GetDeviceScaleFactor() { return 1.0f; } -const base::string16& TestWebUI::GetOverriddenTitle() const { +const base::string16& TestWebUI::GetOverriddenTitle() { return temp_string_; } -int TestWebUI::GetBindings() const { +int TestWebUI::GetBindings() { return bindings_; }
diff --git a/content/public/test/test_web_ui.h b/content/public/test/test_web_ui.h index 68c30218..0e40ae2 100644 --- a/content/public/test/test_web_ui.h +++ b/content/public/test/test_web_ui.h
@@ -29,13 +29,13 @@ } // WebUI overrides. - WebContents* GetWebContents() const override; - WebUIController* GetController() const override; + WebContents* GetWebContents() override; + WebUIController* GetController() override; void SetController(std::unique_ptr<WebUIController> controller) override; - float GetDeviceScaleFactor() const override; - const base::string16& GetOverriddenTitle() const override; + float GetDeviceScaleFactor() override; + const base::string16& GetOverriddenTitle() override; void OverrideTitle(const base::string16& title) override {} - int GetBindings() const override; + int GetBindings() override; void SetBindings(int bindings) override; void AddMessageHandler(std::unique_ptr<WebUIMessageHandler> handler) override; void RegisterMessageCallback(base::StringPiece message,
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index 33817ba2..f46d82f 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc
@@ -3362,10 +3362,12 @@ using ReportTimeCallback = blink::WebWidgetClient::ReportTimeCallback; public: - ReportTimeSwapPromise(ReportTimeCallback callback, + ReportTimeSwapPromise(ReportTimeCallback swap_time_callback, + ReportTimeCallback presentation_time_callback, scoped_refptr<base::SingleThreadTaskRunner> task_runner, base::WeakPtr<RenderWidget> render_widget) - : callback_(std::move(callback)), + : swap_time_callback_(std::move(swap_time_callback)), + presentation_time_callback_(std::move(presentation_time_callback)), task_runner_(std::move(task_runner)), render_widget_(std::move(render_widget)) {} ~ReportTimeSwapPromise() override = default; @@ -3381,21 +3383,10 @@ void DidSwap() override { DCHECK_GT(frame_token_, 0u); task_runner_->PostTask( - FROM_HERE, - base::BindOnce( - [](base::TimeTicks timestamp, ReportTimeCallback callback, - base::WeakPtr<RenderWidget> render_widget, int frame_token) { - std::move(callback).Run( - blink::WebWidgetClient::SwapResult::kDidSwap, timestamp); - if (render_widget) { - render_widget->layer_tree_view()->AddPresentationCallback( - frame_token, - base::BindOnce(&RecordSwapTimeToPresentationTime, - timestamp)); - } - }, - base::TimeTicks::Now(), std::move(callback_), render_widget_, - frame_token_)); + FROM_HERE, base::BindOnce(&RunCallbackAfterSwap, base::TimeTicks::Now(), + std::move(swap_time_callback_), + std::move(presentation_time_callback_), + std::move(render_widget_), frame_token_)); } cc::SwapPromise::DidNotSwapAction DidNotSwap( @@ -3419,14 +3410,45 @@ // using presentation or swap timestamps. task_runner_->PostTask( FROM_HERE, - base::BindOnce(std::move(callback_), result, base::TimeTicks::Now())); + base::BindOnce( + [](blink::WebWidgetClient::SwapResult result, + base::TimeTicks swap_time, ReportTimeCallback swap_time_callback, + ReportTimeCallback presentation_time_callback) { + ReportTime(std::move(swap_time_callback), result, swap_time); + ReportTime(std::move(presentation_time_callback), result, + swap_time); + }, + result, base::TimeTicks::Now(), std::move(swap_time_callback_), + std::move(presentation_time_callback_))); return DidNotSwapAction::BREAK_PROMISE; } int64_t TraceId() const override { return 0; } private: - static void RecordSwapTimeToPresentationTime( + static void RunCallbackAfterSwap( + base::TimeTicks swap_time, + ReportTimeCallback swap_time_callback, + ReportTimeCallback presentation_time_callback, + base::WeakPtr<RenderWidget> render_widget, + int frame_token) { + if (render_widget) { + render_widget->layer_tree_view()->AddPresentationCallback( + frame_token, + base::BindOnce(&RunCallbackAfterPresentation, + std::move(presentation_time_callback), swap_time)); + ReportTime(std::move(swap_time_callback), + blink::WebWidgetClient::SwapResult::kDidSwap, swap_time); + } else { + ReportTime(std::move(swap_time_callback), + blink::WebWidgetClient::SwapResult::kDidSwap, swap_time); + ReportTime(std::move(presentation_time_callback), + blink::WebWidgetClient::SwapResult::kDidSwap, swap_time); + } + } + + static void RunCallbackAfterPresentation( + ReportTimeCallback presentation_time_callback, base::TimeTicks swap_time, base::TimeTicks presentation_time) { DCHECK(!swap_time.is_null()); @@ -3440,9 +3462,20 @@ "PageLoad.Internal.Renderer.PresentationTime.DeltaFromSwapTime", presentation_time - swap_time); } + ReportTime(std::move(presentation_time_callback), + blink::WebWidgetClient::SwapResult::kDidSwap, + presentation_time_is_valid ? presentation_time : swap_time); } - ReportTimeCallback callback_; + static void ReportTime(ReportTimeCallback callback, + blink::WebWidgetClient::SwapResult result, + base::TimeTicks time) { + if (callback) + std::move(callback).Run(result, time); + } + + ReportTimeCallback swap_time_callback_; + ReportTimeCallback presentation_time_callback_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; base::WeakPtr<RenderWidget> render_widget_; uint32_t frame_token_ = 0; @@ -3451,11 +3484,17 @@ }; void RenderWidget::NotifySwapTime(ReportTimeCallback callback) { + NotifySwapAndPresentationTime(base::NullCallback(), std::move(callback)); +} + +void RenderWidget::NotifySwapAndPresentationTime( + ReportTimeCallback swap_time_callback, + ReportTimeCallback presentation_time_callback) { cc::LayerTreeHost* layer_tree_host = layer_tree_view_->layer_tree_host(); // When the WebWidget is closed we cancel any pending SwapPromise that would // call back into blink, so we use |close_weak_ptr_factory_|. layer_tree_host->QueueSwapPromise(std::make_unique<ReportTimeSwapPromise>( - std::move(callback), + std::move(swap_time_callback), std::move(presentation_time_callback), layer_tree_host->GetTaskRunnerProvider()->MainThreadTaskRunner(), close_weak_ptr_factory_.GetWeakPtr())); }
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h index 1115b02..47e137f 100644 --- a/content/renderer/render_widget.h +++ b/content/renderer/render_widget.h
@@ -460,6 +460,16 @@ base::OnceCallback<void(bool)> callback) override; void NotifySwapTime(ReportTimeCallback callback) override; + // Registers a SwapPromise to report presentation time and possibly swap time. + // If |swap_time_callback| is not a null callback, it would be called once + // swap happens. |presentation_time_callback| will be called some time after + // pixels are presented on screen. Swap time is needed only in tests and + // production code uses |NotifySwapTime()| above which calls this one passing + // a null callback as |swap_time_callback|. + void NotifySwapAndPresentationTime( + ReportTimeCallback swap_time_callback, + ReportTimeCallback presentation_time_callback); + // Override point to obtain that the current input method state and caret // position. ui::TextInputType GetTextInputType();
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc index 1b7cf5d..ff458c4 100644 --- a/content/renderer/render_widget_unittest.cc +++ b/content/renderer/render_widget_unittest.cc
@@ -727,35 +727,56 @@ color_layer->SetBackgroundColor(SK_ColorRED); } - base::TimeTicks CompositeAndReturnSwapTimestamp() { + // |swap_to_presentation| determines how long after swap should presentation + // happen. This can be negative, positive, or zero. If zero, an invalid (null) + // presentation time is used. + void CompositeAndWaitForPresentation(base::TimeDelta swap_to_presentation) { + base::RunLoop swap_run_loop; + base::RunLoop presentation_run_loop; + + // Register callbacks for swap time and presentation time. base::TimeTicks swap_time; - base::RunLoop run_loop; - widget()->NotifySwapTime(base::BindOnce( - [](base::OnceClosure callback, base::TimeTicks* swap_time, - blink::WebWidgetClient::SwapResult result, - base::TimeTicks timestamp) { - *swap_time = timestamp; - std::move(callback).Run(); - }, - run_loop.QuitClosure(), &swap_time)); + widget()->NotifySwapAndPresentationTime( + base::BindOnce( + [](base::OnceClosure swap_quit_closure, base::TimeTicks* swap_time, + blink::WebWidgetClient::SwapResult result, + base::TimeTicks timestamp) { + DCHECK(!timestamp.is_null()); + *swap_time = timestamp; + std::move(swap_quit_closure).Run(); + }, + swap_run_loop.QuitClosure(), &swap_time), + base::BindOnce( + [](base::OnceClosure presentation_quit_closure, + blink::WebWidgetClient::SwapResult result, + base::TimeTicks timestamp) { + DCHECK(!timestamp.is_null()); + std::move(presentation_quit_closure).Run(); + }, + presentation_run_loop.QuitClosure())); + + // Composite and wait for the swap to complete. widget()->layer_tree_view()->layer_tree_host()->Composite( - base::TimeTicks::Now(), /*raster=*/true); - // The swap time notify comes as a posted task. - run_loop.Run(); - return swap_time; + base::TimeTicks::Now(), + /*raster=*/true); + swap_run_loop.Run(); + + // Present and wait for it to complete. + base::TimeTicks presentation_time; + if (!swap_to_presentation.is_zero()) + presentation_time = swap_time + swap_to_presentation; + widget()->layer_tree_view()->DidPresentCompositorFrame( + 1, gfx::PresentationFeedback(presentation_time, + base::TimeDelta::FromMilliseconds(16), 0)); + presentation_run_loop.Run(); } }; TEST_F(NotifySwapTimesRenderWidgetUnittest, PresentationTimestampValid) { base::HistogramTester histograms; - base::TimeTicks swap_time = CompositeAndReturnSwapTimestamp(); - ASSERT_FALSE(swap_time.is_null()); + CompositeAndWaitForPresentation(base::TimeDelta::FromMilliseconds(2)); - widget()->layer_tree_view()->DidPresentCompositorFrame( - 1, gfx::PresentationFeedback( - swap_time + base::TimeDelta::FromMilliseconds(2), - base::TimeDelta::FromMilliseconds(16), 0)); EXPECT_THAT(histograms.GetAllSamples( "PageLoad.Internal.Renderer.PresentationTime.Valid"), testing::ElementsAre(base::Bucket(true, 1))); @@ -768,11 +789,8 @@ TEST_F(NotifySwapTimesRenderWidgetUnittest, PresentationTimestampInvalid) { base::HistogramTester histograms; - base::TimeTicks swap_time = CompositeAndReturnSwapTimestamp(); - ASSERT_FALSE(swap_time.is_null()); + CompositeAndWaitForPresentation(base::TimeDelta()); - widget()->layer_tree_view()->DidPresentCompositorFrame( - 1, gfx::PresentationFeedback()); EXPECT_THAT(histograms.GetAllSamples( "PageLoad.Internal.Renderer.PresentationTime.Valid"), testing::ElementsAre(base::Bucket(false, 1))); @@ -786,13 +804,8 @@ PresentationTimestampEarlierThanSwaptime) { base::HistogramTester histograms; - base::TimeTicks swap_time = CompositeAndReturnSwapTimestamp(); - ASSERT_FALSE(swap_time.is_null()); + CompositeAndWaitForPresentation(base::TimeDelta::FromMilliseconds(-2)); - widget()->layer_tree_view()->DidPresentCompositorFrame( - 1, gfx::PresentationFeedback( - swap_time - base::TimeDelta::FromMilliseconds(2), - base::TimeDelta::FromMilliseconds(16), 0)); EXPECT_THAT(histograms.GetAllSamples( "PageLoad.Internal.Renderer.PresentationTime.Valid"), testing::ElementsAre(base::Bucket(false, 1)));
diff --git a/content/shell/browser/web_test/web_test_push_messaging_service.cc b/content/shell/browser/web_test/web_test_push_messaging_service.cc index 210c950f..528f625 100644 --- a/content/shell/browser/web_test/web_test_push_messaging_service.cc +++ b/content/shell/browser/web_test/web_test_push_messaging_service.cc
@@ -49,7 +49,7 @@ WebTestPushMessagingService::~WebTestPushMessagingService() {} -GURL WebTestPushMessagingService::GetEndpoint(bool standard_protocol) const { +GURL WebTestPushMessagingService::GetEndpoint(bool standard_protocol) { return GURL(standard_protocol ? "https://example.com/StandardizedEndpoint/" : "https://example.com/LayoutTestEndpoint/"); }
diff --git a/content/shell/browser/web_test/web_test_push_messaging_service.h b/content/shell/browser/web_test/web_test_push_messaging_service.h index fb36035f..b2a10fd 100644 --- a/content/shell/browser/web_test/web_test_push_messaging_service.h +++ b/content/shell/browser/web_test/web_test_push_messaging_service.h
@@ -25,7 +25,7 @@ ~WebTestPushMessagingService() override; // PushMessagingService implementation: - GURL GetEndpoint(bool standard_protocol) const override; + GURL GetEndpoint(bool standard_protocol) override; void SubscribeFromDocument(const GURL& requesting_origin, int64_t service_worker_registration_id, int renderer_id,
diff --git a/content/test/mock_ssl_host_state_delegate.cc b/content/test/mock_ssl_host_state_delegate.cc index 324db081..744494b 100644 --- a/content/test/mock_ssl_host_state_delegate.cc +++ b/content/test/mock_ssl_host_state_delegate.cc
@@ -52,7 +52,7 @@ bool MockSSLHostStateDelegate::DidHostRunInsecureContent( const std::string& host, int child_id, - InsecureContentType content_type) const { + InsecureContentType content_type) { return false; } @@ -61,8 +61,7 @@ exceptions_.erase(exceptions_.find(host)); } -bool MockSSLHostStateDelegate::HasAllowException( - const std::string& host) const { +bool MockSSLHostStateDelegate::HasAllowException(const std::string& host) { return exceptions_.find(host) != exceptions_.end(); }
diff --git a/content/test/mock_ssl_host_state_delegate.h b/content/test/mock_ssl_host_state_delegate.h index 8205ef1..45e3efc 100644 --- a/content/test/mock_ssl_host_state_delegate.h +++ b/content/test/mock_ssl_host_state_delegate.h
@@ -30,14 +30,13 @@ int child_id, InsecureContentType content_type) override; - bool DidHostRunInsecureContent( - const std::string& host, - int child_id, - InsecureContentType content_type) const override; + bool DidHostRunInsecureContent(const std::string& host, + int child_id, + InsecureContentType content_type) override; void RevokeUserAllowExceptions(const std::string& host) override; - bool HasAllowException(const std::string& host) const override; + bool HasAllowException(const std::string& host) override; private: std::set<std::string> exceptions_;
diff --git a/device/bluetooth/bluetooth_adapter_unittest.cc b/device/bluetooth/bluetooth_adapter_unittest.cc index f5e9dde..78c66a00 100644 --- a/device/bluetooth/bluetooth_adapter_unittest.cc +++ b/device/bluetooth/bluetooth_adapter_unittest.cc
@@ -79,9 +79,6 @@ bool IsDiscovering() const override { return false; } - void StartDiscoverySession(const DiscoverySessionCallback& callback, - const ErrorCallback& error_callback) {} - UUIDList GetUUIDs() const override { return UUIDList(); } void CreateRfcommService(
diff --git a/extensions/browser/OWNERS b/extensions/browser/OWNERS index c21998b..1893077 100644 --- a/extensions/browser/OWNERS +++ b/extensions/browser/OWNERS
@@ -1,7 +1,6 @@ # Please talk to the apps shell team before adding DEPS. per-file DEPS=set noparent per-file DEPS=benwells@chromium.org -per-file DEPS=derat@chromium.org per-file DEPS=rockot@google.com per-file DEPS=rdevlin.cronin@chromium.org
diff --git a/extensions/browser/api/power/OWNERS b/extensions/browser/api/power/OWNERS deleted file mode 100644 index 3c97e54..0000000 --- a/extensions/browser/api/power/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -derat@chromium.org
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.cc index 3e3c723..0820b76 100644 --- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.cc +++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_embedder.cc
@@ -218,8 +218,13 @@ } void MimeHandlerViewEmbedder::CheckSandboxFlags() { - if (!render_frame_host_->IsSandboxed(blink::WebSandboxFlags::kPlugins)) + // If the FrameTreeNode is deleted while it has ownership of the ongoing + // NavigationRequest, DidFinishNavigation is called before FrameDeleted (see + // https://crbug.com/969840). + if (render_frame_host_ && + !render_frame_host_->IsSandboxed(blink::WebSandboxFlags::kPlugins)) { return; + } // Notify the renderer to load an empty page instead. GetContainerManager()->LoadEmptyPage(resource_url_); GetMimeHandlerViewEmbeddersMap()->erase(frame_tree_node_id_);
diff --git a/gpu/command_buffer/client/raster_implementation.cc b/gpu/command_buffer/client/raster_implementation.cc index 37b9361..2328f1f 100644 --- a/gpu/command_buffer/client/raster_implementation.cc +++ b/gpu/command_buffer/client/raster_implementation.cc
@@ -91,21 +91,6 @@ const uint32_t kMaxTransferCacheEntrySizeForTransferBuffer = 1024; -void RecordPaintOpSize(size_t size) { - constexpr size_t kMinPaintOpSize = 512 * 1024; - constexpr size_t kMaxPaintOpSize = 16 * 1024 * 1024; - - // Serialization failure, record max size. - if (size == 0u) - size = kMaxPaintOpSize; - - if (size < kMinPaintOpSize) - return; - - UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.OopRaster.PaintOpSerializationSize", size, - kMinPaintOpSize, kMaxPaintOpSize, 50); -} - } // namespace // Helper to copy data to the GPU service over the transfer cache. @@ -258,13 +243,11 @@ } if (!size) { - RecordPaintOpSize(0u); LOG(ERROR) << "Failed to serialize op in " << block_size << " bytes."; return 0u; } } - RecordPaintOpSize(size); DCHECK_LE(size, free_bytes_); DCHECK(base::CheckAdd<uint32_t>(written_bytes_, size).IsValid());
diff --git a/gpu/command_buffer/client/raster_interface.h b/gpu/command_buffer/client/raster_interface.h index 74646c7..ccd2db38 100644 --- a/gpu/command_buffer/client/raster_interface.h +++ b/gpu/command_buffer/client/raster_interface.h
@@ -57,7 +57,9 @@ const gfx::ColorSpace& color_space, const GLbyte* mailbox) = 0; - static constexpr size_t kDefaultMaxOpSizeHint = 512 * 1024; + // Heuristic decided on UMA data. This covers 85% of the cases where we need + // to serialize ops > 512k. + static constexpr size_t kDefaultMaxOpSizeHint = 600 * 1024; virtual void RasterCHROMIUM(const cc::DisplayItemList* list, cc::ImageProvider* provider, const gfx::Size& content_size,
diff --git a/gpu/config/gpu_info.h b/gpu/config/gpu_info.h index 8aef772..0e35bc60 100644 --- a/gpu/config/gpu_info.h +++ b/gpu/config/gpu_info.h
@@ -80,7 +80,9 @@ AV1PROFILE_PROFILE_MAIN, AV1PROFILE_PROFILE_HIGH, AV1PROFILE_PROFILE_PRO, - VIDEO_CODEC_PROFILE_MAX = AV1PROFILE_PROFILE_PRO, + DOLBYVISION_PROFILE8, + DOLBYVISION_PROFILE9, + VIDEO_CODEC_PROFILE_MAX = DOLBYVISION_PROFILE9, }; // Specification of a decoding profile supported by a hardware decoder.
diff --git a/gpu/ipc/common/gpu_info.mojom b/gpu/ipc/common/gpu_info.mojom index 954c46eb..9705ef1 100644 --- a/gpu/ipc/common/gpu_info.mojom +++ b/gpu/ipc/common/gpu_info.mojom
@@ -52,6 +52,8 @@ AV1PROFILE_PROFILE_MAIN, AV1PROFILE_PROFILE_HIGH, AV1PROFILE_PROFILE_PRO, + DOLBYVISION_PROFILE8, + DOLBYVISION_PROFILE9, }; // gpu::VideoDecodeAcceleratorSupportedProfile
diff --git a/gpu/ipc/common/gpu_info_struct_traits.cc b/gpu/ipc/common/gpu_info_struct_traits.cc index 64ade57..28d5662 100644 --- a/gpu/ipc/common/gpu_info_struct_traits.cc +++ b/gpu/ipc/common/gpu_info_struct_traits.cc
@@ -79,6 +79,10 @@ return gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE5; case gpu::VideoCodecProfile::DOLBYVISION_PROFILE7: return gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE7; + case gpu::VideoCodecProfile::DOLBYVISION_PROFILE8: + return gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE8; + case gpu::VideoCodecProfile::DOLBYVISION_PROFILE9: + return gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE9; case gpu::VideoCodecProfile::THEORAPROFILE_ANY: return gpu::mojom::VideoCodecProfile::THEORAPROFILE_ANY; case gpu::VideoCodecProfile::AV1PROFILE_PROFILE_MAIN: @@ -169,6 +173,12 @@ case gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE7: *out = gpu::VideoCodecProfile::DOLBYVISION_PROFILE7; return true; + case gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE8: + *out = gpu::VideoCodecProfile::DOLBYVISION_PROFILE8; + return true; + case gpu::mojom::VideoCodecProfile::DOLBYVISION_PROFILE9: + *out = gpu::VideoCodecProfile::DOLBYVISION_PROFILE9; + return true; case gpu::mojom::VideoCodecProfile::THEORAPROFILE_ANY: *out = gpu::VideoCodecProfile::THEORAPROFILE_ANY; return true;
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg index f842614..6ab0bbb 100644 --- a/infra/config/cr-buildbucket.cfg +++ b/infra/config/cr-buildbucket.cfg
@@ -1039,28 +1039,28 @@ builders { name: "Android WebView L (dbg)" - mixins: "android-ci" + mixins: "android-ci-goma-rbe-prod" mixins: "builderless" dimensions: "os:Ubuntu-14.04" } builders { name: "Android WebView M (dbg)" - mixins: "android-ci" + mixins: "android-ci-goma-rbe-prod" mixins: "builderless" dimensions: "os:Ubuntu-14.04" } builders { name: "Android WebView N (dbg)" - mixins: "android-ci" + mixins: "android-ci-goma-rbe-prod" mixins: "builderless" dimensions: "os:Ubuntu-14.04" } builders { name: "Android WebView O (dbg)" - mixins: "android-ci" + mixins: "android-ci-goma-rbe-prod" mixins: "builderless" dimensions: "os:Ubuntu-14.04" } @@ -1069,12 +1069,13 @@ name: "Android WebView O NetworkService (dbg)" mixins: "android-fyi-ci" mixins: "builderless" + mixins: "goma-rbe-prod" dimensions: "os:Ubuntu-14.04" } builders { name: "Android x64 Builder (dbg)" - mixins: "android-ci" + mixins: "android-ci-goma-rbe-prod" mixins: "builderless" dimensions: "os:Ubuntu-14.04" execution_timeout_secs: 14400 # 4h @@ -1082,7 +1083,7 @@ builders { name: "Android x86 Builder (dbg)" - mixins: "android-ci" + mixins: "android-ci-goma-rbe-prod" mixins: "builderless" dimensions: "os:Ubuntu-14.04" } @@ -1095,6 +1096,13 @@ } builders { + name: "android-code-coverage" + mixins: "code-coverage" + mixins: "linux" + dimensions: "os:Ubuntu-14.04" + } + + builders { name: "android-cronet-arm-dbg" mixins: "android-ci" dimensions: "os:Ubuntu-14.04" @@ -2352,6 +2360,7 @@ dimensions: "os:Mac-10.13" dimensions: "cores:4" mixins: "fuzz-ci" + mixins: "goma-rbe-prod" } builders { name: "Libfuzzer Upload Linux MSan" @@ -2374,6 +2383,7 @@ dimensions: "os:Mac-10.13" dimensions: "cores:4" mixins: "fuzz-ci" + mixins: "goma-rbe-prod" } builders { name: "WebKit Linux ASAN" @@ -2386,6 +2396,7 @@ dimensions: "cores:24" mixins: "fuzz-ci" mixins: "libfuzzer" + mixins: "goma-rbe-prod" execution_timeout_secs: 14400 # 4 hours } builders { @@ -2402,7 +2413,13 @@ mixins: "memory-ci" # TODO(hinoka): Remove this after debugging. recipe { - properties_j: "$build/goma:{\"debug\": true}" + properties_j: <<END + $build/goma: { + "server_host": "goma.chromium.org", + "rpc_extra_params": "?prod", + "debug": true + } + END } } builders { @@ -2480,6 +2497,7 @@ name: "WebKit Mac10.13 (retina)" dimensions: "os:Mac-10.13" mixins: "mac-ci" + mixins: "goma-rbe-prod" } builders { name: "Afl Upload Linux ASan" @@ -2526,6 +2544,7 @@ name: "Site Isolation Android" dimensions: "os:Ubuntu-14.04" mixins: "fyi-ci" + mixins: "goma-rbe-prod" } builders { name: "MSAN Release (no origins)" @@ -2614,6 +2633,7 @@ dimensions: "os:Mac-10.13" dimensions: "cores:4" mixins: "fyi-ci" + mixins: "goma-rbe-prod" } builders { name: "linux-archive-dbg" @@ -2682,7 +2702,7 @@ # TODO(https://crbug.com/919430) Remove the larger timeout once compile # times have been brought down to reasonable level execution_timeout_secs: 16200 # 4.5h - mixins: "memory-ci" + mixins: "memory-ci-goma-rbe-prod" } builders { name: "chromeos-amd64-generic-rel-goma-canary" @@ -2815,6 +2835,7 @@ name: "Mojo Android" dimensions: "os:Ubuntu-14.04" mixins: "fyi-ci" + mixins: "goma-rbe-prod" } # Goma RBE ToT/Staging/FYI builders { @@ -3511,6 +3532,7 @@ builders { mixins: "android-optional-gpu-try" name: "android_optional_gpu_tests_rel" } builders { mixins: "android-try" + mixins: "goma-rbe-prod" name: "android_unswarmed_pixel_aosp" dimensions: "os:Ubuntu-14.04" } @@ -3904,11 +3926,15 @@ # The 10.xx version translates to which bots will run isolated tests. builders { mixins: "mac-try" name: "mac_chromium_10.10" } builders { mixins: "mac-try" name: "mac_chromium_10.12_rel_ng" } - builders { mixins: "mac-try" name: "mac_chromium_10.13_rel_ng"} + builders { + mixins: "mac-try" + mixins: "goma-rbe-prod" + name: "mac_chromium_10.13_rel_ng" + } builders { mixins: "mac-try" name: "mac_chromium_archive_rel_ng" } builders { mixins: "mac-try" - mixins: "goma-j150" + mixins: "goma-rbe-prod-j150" name: "mac_chromium_asan_rel_ng" } builders { @@ -4058,6 +4084,7 @@ name: "android_compile_x86_dbg" dimensions: "os:Ubuntu-14.04" mixins: "android-try" + mixins: "goma-rbe-prod" } builders { name: "linux-annotator-rel" @@ -4084,6 +4111,7 @@ name: "android_compile_x64_dbg" dimensions: "os:Ubuntu-14.04" mixins: "android-try" + mixins: "goma-rbe-prod" } builders { name: "android_archive_rel_ng" @@ -4101,11 +4129,13 @@ dimensions: "os:Ubuntu-14.04" dimensions: "cores:32" mixins: "android-try" + mixins: "goma-rbe-prod" } builders { name: "android_mojo" dimensions: "os:Ubuntu-14.04" mixins: "android-try" + mixins: "goma-rbe-prod" } # Blink try builders. @@ -4138,6 +4168,7 @@ name: "mac10.13_retina-blink-rel" mixins: "mac" mixins: "blink-try" + mixins: "goma-rbe-prod" } builders { name: "win10-blink-rel" @@ -4181,22 +4212,40 @@ # Android - builders { mixins: "linux-xenial" name: "WebRTC Chromium Android Builder" } - builders { mixins: "linux-xenial" name: "WebRTC Chromium Android Tester" } + builders { + name: "WebRTC Chromium Android Builder" + mixins: "linux-xenial" + mixins: "goma-rbe-prod" + } + builders { + name: "WebRTC Chromium Android Tester" + mixins: "linux-xenial" + } # Linux - builders { mixins: "linux-xenial" name: "WebRTC Chromium Linux Builder" } - builders { mixins: "linux" name: "WebRTC Chromium Linux Tester" } + builders { + name: "WebRTC Chromium Linux Builder" + mixins: "linux-xenial" + mixins: "goma-rbe-prod" + } + builders { + name: "WebRTC Chromium Linux Tester" + mixins: "linux" + } # Mac builders { name: "WebRTC Chromium Mac Builder" mixins: "mac" + mixins: "goma-rbe-prod" dimensions: "cores:8" } - builders { mixins: "mac" name: "WebRTC Chromium Mac Tester" } + builders { + name: "WebRTC Chromium Mac Tester" + mixins: "mac" + } # Win @@ -4234,9 +4283,9 @@ # Android - builders { mixins: "linux-xenial" name: "WebRTC Chromium FYI Android Builder" } - builders { mixins: "linux-xenial" name: "WebRTC Chromium FYI Android Builder (dbg)" } - builders { mixins: "linux-xenial" name: "WebRTC Chromium FYI Android Builder ARM64 (dbg)" } + builders { mixins: "linux-xenial" mixins: "goma-rbe-prod" name: "WebRTC Chromium FYI Android Builder" } + builders { mixins: "linux-xenial" mixins: "goma-rbe-prod" name: "WebRTC Chromium FYI Android Builder (dbg)" } + builders { mixins: "linux-xenial" mixins: "goma-rbe-prod" name: "WebRTC Chromium FYI Android Builder ARM64 (dbg)" } builders { mixins: "linux-xenial" name: "WebRTC Chromium FYI Android Tests (dbg) (K Nexus5)" } builders { mixins: "linux-xenial" name: "WebRTC Chromium FYI Android Tests (dbg) (M Nexus5X)" } @@ -4247,8 +4296,8 @@ # Linux - builders { mixins: "linux-xenial" name: "WebRTC Chromium FYI Linux Builder" } - builders { mixins: "linux-xenial" name: "WebRTC Chromium FYI Linux Builder (dbg)" } + builders { mixins: "linux-xenial" mixins: "goma-rbe-prod" name: "WebRTC Chromium FYI Linux Builder" } + builders { mixins: "linux-xenial" mixins: "goma-rbe-prod" name: "WebRTC Chromium FYI Linux Builder (dbg)" } builders { mixins: "linux-xenial" name: "WebRTC Chromium FYI Linux Tester" } # Mac @@ -4256,11 +4305,13 @@ builders { name: "WebRTC Chromium FYI Mac Builder" mixins: "mac" + mixins: "goma-rbe-prod" dimensions: "cores:8" } builders { name: "WebRTC Chromium FYI Mac Builder (dbg)" mixins: "mac" + mixins: "goma-rbe-prod" dimensions: "cores:8" } builders { mixins: "mac" name: "WebRTC Chromium FYI Mac Tester" }
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg index 837936b..dc391cd 100644 --- a/infra/config/luci-milo.cfg +++ b/infra/config/luci-milo.cfg
@@ -2049,6 +2049,11 @@ category: "closure_compilation" } builders { + name: "buildbucket/luci.chromium.ci/android-code-coverage" + category: "code_coverage" + short_name: "and" + } + builders { name: "buildbucket/luci.chromium.ci/linux-code-coverage" category: "code_coverage" short_name: "lnx" @@ -2816,6 +2821,85 @@ category: "week2b|mac" short_name: "bld" } + builders { + name: "buildbucket/luci.chromium.ci/Chromium Mac 10.13" + category: "week2c|mac" + short_name: "10.13" + } + builders { + name: "buildbucket/luci.chromium.ci/Mac ASAN Release" + category: "week2c|mac|asan" + } + builders { + name: "buildbucket/luci.chromium.ci/Mac ASAN Release Media" + category: "week2c|mac|asan" + short_name: "media" + } + builders { + name: "buildbucket/luci.chromium.ci/Mac ASan 64 Builder" + category: "week2c|mac|asan" + short_name: "64" + } + builders { + name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Mac ASan" + category: "week2c|mac|asan" + short_name: "fuzz" + } + builders { + name: "buildbucket/luci.chromium.ci/WebKit Mac10.13 (retina)" + category: "week2c|mac" + short_name: "webkit" + } + builders { + name: "buildbucket/luci.chromium.ci/Android CFI" + category: "week2c|android" + short_name: "cfi" + } + builders { + name: "buildbucket/luci.chromium.ci/Site Isolation Android" + category: "week2c|android" + short_name: "isolate" + } + builders { + name: "buildbucket/luci.chromium.ci/Mojo Android" + category: "week2c|android" + short_name: "mojo" + } + builders { + name: "buildbucket/luci.chromium.ci/Android x64 Builder (dbg)" + category: "week2c|android|dbg" + short_name: "x64" + } + builders { + name: "buildbucket/luci.chromium.ci/Android x86 Builder (dbg)" + category: "week2c|android|dbg" + short_name: "x86" + } + builders { + name: "buildbucket/luci.chromium.ci/Android WebView L (dbg)" + category: "week2c|android|dbg|webview" + short_name: "l" + } + builders { + name: "buildbucket/luci.chromium.ci/Android WebView M (dbg)" + category: "week2c|android|dbg|webview" + short_name: "m" + } + builders { + name: "buildbucket/luci.chromium.ci/Android WebView N (dbg)" + category: "week2c|android|dbg|webview" + short_name: "n" + } + builders { + name: "buildbucket/luci.chromium.ci/Android WebView O (dbg)" + category: "week2c|android|dbg|webview" + short_name: "o" + } + builders { + name: "buildbucket/luci.chromium.ci/Android WebView O NetworkService (dbg)" + category: "week2c|android|dbg|webview" + short_name: "o net" + } } consoles {
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg index 6a98f4f..9ff02b0 100644 --- a/infra/config/luci-scheduler.cfg +++ b/infra/config/luci-scheduler.cfg
@@ -299,6 +299,7 @@ triggers: "WinMSVC64 Goma Latest Client" triggers: "Windows deterministic" triggers: "android-asan" + triggers: "android-code-coverage" triggers: "android-cronet-arm-dbg" triggers: "android-cronet-arm-rel" triggers: "android-cronet-arm64-dbg" @@ -701,6 +702,16 @@ } job { + id: "android-code-coverage" + acl_sets: "default" + buildbucket: { + server: "cr-buildbucket.appspot.com" + bucket: "luci.chromium.ci" + builder: "android-code-coverage" + } +} + +job { id: "android-cronet-arm-dbg" acl_sets: "default" buildbucket: {
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm index dbb026e7..e16029e 100644 --- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm +++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -3539,10 +3539,19 @@ [SizeClassRecorder pageLoadedWithHorizontalSizeClass:sizeClass]; } - // If there is no first responder, try to make the webview the first + // If there is no first responder, try to make the webview or the NTP first // responder to have it answer keyboard commands (e.g. space bar to scroll). - if (!GetFirstResponder() && self.currentWebState) - [self.currentWebState->GetWebViewProxy() becomeFirstResponder]; + if (!GetFirstResponder() && self.currentWebState) { + NewTabPageTabHelper* NTPHelper = + NewTabPageTabHelper::FromWebState(webState); + if (NTPHelper && NTPHelper->IsActive()) { + UIViewController* viewController = + _ntpCoordinatorsForWebStates[webState].viewController; + [viewController becomeFirstResponder]; + } else { + [self.currentWebState->GetWebViewProxy() becomeFirstResponder]; + } + } } #pragma mark - OmniboxPopupPresenterDelegate methods.
diff --git a/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.h b/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.h index c2e1ce3..ebdad37 100644 --- a/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.h +++ b/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.h
@@ -10,9 +10,6 @@ // A button for an Infobar that contains a badge image. @interface InfobarBadgeButton : ExtendedTouchTargetButton -// Gives the badge a dark gray background if |selected| is YES. Removes the -// background if |selected| is NO. Will animate change if |animated| is YES. -- (void)setSelected:(BOOL)selected animated:(BOOL)animated; // Sets the badge color to blue if |active| is YES, light gray if |active| is // NO. Will animate change if |animated| is YES. - (void)setActive:(BOOL)active animated:(BOOL)animated;
diff --git a/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.mm b/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.mm index 3801c38..5c5d610 100644 --- a/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.mm +++ b/ios/chrome/browser/ui/infobars/badge/infobar_badge_button.mm
@@ -15,8 +15,6 @@ const CGFloat kButtonAnimationDuration = 0.2; // Edge insets of button. const CGFloat kButtonEdgeInset = 6; -// White value of the button background in a selected state. -const CGFloat kSelectedWhiteValue = 0.80; // Tint color of the button in an active state. const CGFloat kActiveTintColor = 0x1A73E8; // To achieve a circular corner radius, divide length of a side by 2. @@ -50,21 +48,6 @@ self.bounds.size.height / kCircularCornerRadiusDivisor; } -- (void)setSelected:(BOOL)selected animated:(BOOL)animated { - void (^changeBackgroundColor)() = ^{ - self.backgroundColor = - selected ? [UIColor colorWithWhite:kSelectedWhiteValue alpha:1.0] - : [UIColor clearColor]; - }; - if (animated) { - [UIView animateWithDuration:kButtonAnimationDuration - animations:^{ - changeBackgroundColor(); - }]; - } else { - changeBackgroundColor(); - } -} - (void)setActive:(BOOL)active animated:(BOOL)animated { void (^changeTintColor)() = ^{ self.tintColor =
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_consumer.h b/ios/chrome/browser/ui/location_bar/location_bar_consumer.h index aa2365c3..2df04ea 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_consumer.h +++ b/ios/chrome/browser/ui/location_bar/location_bar_consumer.h
@@ -39,11 +39,6 @@ // Infobar redesign. - (void)displayInfobarBadge:(BOOL)display type:(InfobarType)infobarType; -// Notifies the consumer that the InfobarBadge select state has changed. -// TODO(crbug.com/935804): This method is currently only being used in the -// Infobar redesign. -- (void)selectInfobarBadge:(BOOL)select; - // Notifies the consumer that the InfobarBadge active state has changed. // TODO(crbug.com/935804): This method is currently only being used in the // Infobar redesign.
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm index 32b84d13..bd12175 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm +++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -393,10 +393,6 @@ metricsRecorder:metricsRecorder]; } -- (void)selectInfobarBadge:(BOOL)select { - [self.viewController setInfobarButtonStyleSelected:select]; -} - - (void)activeInfobarBadge:(BOOL)active { [self.viewController setInfobarButtonStyleActive:active]; }
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm b/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm index 6fd8541..a74a7a2 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm +++ b/ios/chrome/browser/ui/location_bar/location_bar_mediator.mm
@@ -168,7 +168,6 @@ - (void)setBadgeState:(InfobarBadgeState)badgeState { _badgeState = badgeState; - [self.consumer selectInfobarBadge:_badgeState & InfobarBadgeStateSelected]; [self.consumer activeInfobarBadge:_badgeState & InfobarBadgeStateAccepted]; }
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h index 10374c6..0a6c718f 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h +++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.h
@@ -81,11 +81,6 @@ // Infobar redesign. - (void)displayInfobarButton:(BOOL)display metricsRecorder:(InfobarMetricsRecorder*)metricsRecorder; -// If |selected| is YES applies the selected styling to the InfobarButton, if NO -// it removes it. -// TODO(crbug.com/935804): This method is currently only being used in the -// Infobar redesign. -- (void)setInfobarButtonStyleSelected:(BOOL)selected; // If |active| is YES applies the active styling to the InfobarButton, if NO it // removes it. // TODO(crbug.com/935804): This method is currently only being used in the
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm index 325b0cf..11570d0 100644 --- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm +++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
@@ -482,10 +482,6 @@ [self.dispatcher displayModalInfobar]; } -- (void)setInfobarButtonStyleSelected:(BOOL)selected { - [self.locationBarSteadyView.leadingButton setSelected:selected animated:YES]; -} - - (void)setInfobarButtonStyleActive:(BOOL)active { self.activeBadge = active; [self.locationBarSteadyView.leadingButton setActive:active animated:YES];
diff --git a/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm b/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm index 2cdc4d73..b6f6da9 100644 --- a/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm +++ b/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
@@ -491,7 +491,7 @@ @"There should be 1 typed URL entity"); // Delete typed URL from client. - [ChromeEarlGrey deleteTypedURL:mockURL]; + [ChromeEarlGrey deleteHistoryServiceTypedURL:mockURL]; // Trigger sync and wait for typed URL to be deleted. [ChromeEarlGrey triggerSyncCycleForType:syncer::TYPED_URLS]; @@ -525,7 +525,7 @@ name:mockURL.spec() count:1 timeout:kSyncOperationTimeout]); - [ChromeEarlGrey deleteTypedURL:mockURL]; + [ChromeEarlGrey deleteHistoryServiceTypedURL:mockURL]; // Trigger sync and wait for fake server to be updated. [ChromeEarlGrey triggerSyncCycleForType:syncer::TYPED_URLS];
diff --git a/ios/chrome/browser/web/forms_egtest.mm b/ios/chrome/browser/web/forms_egtest.mm index 7b33e06..d9aa8b7 100644 --- a/ios/chrome/browser/web/forms_egtest.mm +++ b/ios/chrome/browser/web/forms_egtest.mm
@@ -280,6 +280,11 @@ // Tests that a POST followed by tapping back to the form page and then tapping // forward to the result page resends data. - (void)testRepostFormAfterTappingBackAndForward { + // TODO(crbug.com/968296): Test is failing on iPad for slim nav. + if (IsIPadIdiom() && web::GetWebClient()->IsSlimNavigationManagerEnabled()) { + EARL_GREY_TEST_DISABLED(@"Test disabled on iPad."); + } + [self setUpFormTestSimpleHttpServer]; const GURL destinationURL = GetDestinationUrl();
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h index 30c7d00f..8bc3949 100644 --- a/ios/chrome/test/earl_grey/chrome_earl_grey.h +++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -152,6 +152,9 @@ // Adds typed URL into HistoryService. - (void)addHistoryServiceTypedURL:(const GURL&)URL; +// Deletes typed URL from HistoryService. +- (void)deleteHistoryServiceTypedURL:(const GURL&)URL; + // Injects a bookmark with |URL| and |title| into the fake sync server. - (void)addFakeSyncServerBookmarkWithURL:(const GURL&)URL title:(const std::string&)title; @@ -184,6 +187,14 @@ count:(size_t)count timeout:(NSTimeInterval)timeout; +// Induces a GREYAssert if |expected_present| is YES and the provided |url| is +// not present, or vice versa. +// TODO(crbug.com/963613): Change return type to void when +// CHROME_EG_ASSERT_NO_ERROR is removed. +- (NSError*)waitForTypedURL:(const GURL&)URL + expectPresent:(BOOL)expectPresent + timeout:(NSTimeInterval)timeout; + #pragma mark - Tab Utilities (EG2) // Opens a new tab and waits for the new tab animation to complete within a @@ -376,27 +387,12 @@ // CHROME_EG_ASSERT_NO_ERROR is removed. - (NSError*)waitForStaticHTMLViewNotContainingText:(NSString*)text; -// Waits for a Chrome error page. -// If the condition is not met within a timeout returns an NSError indicating -// why the operation failed, otherwise nil. -- (NSError*)waitForErrorPage WARN_UNUSED_RESULT; - #pragma mark - Sync Utilities // Injects a bookmark into the fake sync server with |URL| and |title|. - (void)injectBookmarkOnFakeSyncServerWithURL:(const std::string&)URL bookmarkTitle:(const std::string&)title; -// If the provided |url| is present (or not) if |expected_present| -// is YES (or NO) returns nil, otherwise an NSError indicating why the operation -// failed. -- (NSError*)waitForTypedURL:(const GURL&)URL - expectPresent:(BOOL)expectPresent - timeout:(NSTimeInterval)timeout WARN_UNUSED_RESULT; - -// Deletes typed URL from HistoryService. -- (void)deleteTypedURL:(const GURL&)URL; - @end #endif // IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_H_
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm index 54f114e4..ec6c24c9 100644 --- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm +++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -19,7 +19,6 @@ #if defined(CHROME_EARL_GREY_1) #import <WebKit/WebKit.h> -#include "components/strings/grit/components_strings.h" // nogncheck #import "ios/chrome/browser/ui/static_content/static_html_view_controller.h" // nogncheck #import "ios/chrome/test/app/chrome_test_util.h" // nogncheck #import "ios/chrome/test/app/history_test_util.h" // nogncheck @@ -32,7 +31,6 @@ #import "ios/web/public/test/web_view_content_test_util.h" // nogncheck #import "ios/web/public/test/web_view_interaction_test_util.h" // nogncheck #import "ios/web/public/web_state/web_state.h" // nogncheck -#include "ui/base/l10n/l10n_util.h" // nogncheck #endif using base::test::ios::kWaitForJSCompletionTimeout; @@ -41,7 +39,10 @@ using base::test::ios::WaitUntilConditionOrTimeout; namespace { -NSString* kWaitForPageToFinishLoadingError = @"Page did not finish loading"; +NSString* const kWaitForPageToFinishLoadingError = + @"Page did not finish loading"; +NSString* const kTypedURLError = + @"Error occurred during typed URL verification."; } #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -452,6 +453,29 @@ [ChromeEarlGreyAppInterface addHistoryServiceTypedURL:spec]; } +- (void)deleteHistoryServiceTypedURL:(const GURL&)URL { + NSString* spec = base::SysUTF8ToNSString(URL.spec()); + [ChromeEarlGreyAppInterface deleteHistoryServiceTypedURL:spec]; +} + +- (NSError*)waitForTypedURL:(const GURL&)URL + expectPresent:(BOOL)expectPresent + timeout:(NSTimeInterval)timeout { + NSString* spec = base::SysUTF8ToNSString(URL.spec()); + GREYCondition* waitForTypedURL = + [GREYCondition conditionWithName:kTypedURLError + block:^{ + return [ChromeEarlGreyAppInterface + isTypedURL:spec + presentOnClient:expectPresent]; + }]; + + bool success = [waitForTypedURL waitWithTimeout:timeout]; + EG_TEST_HELPER_ASSERT_TRUE(success, kTypedURLError); + + return nil; +} + - (void)triggerSyncCycleForType:(syncer::ModelType)type { [ChromeEarlGreyAppInterface triggerSyncCycleForType:type]; } @@ -576,12 +600,6 @@ #pragma mark - Navigation Utilities -- (NSError*)waitForErrorPage { - NSString* const kErrorPageText = - l10n_util::GetNSString(IDS_ERRORPAGES_HEADING_NOT_AVAILABLE); - return [self waitForStaticHTMLViewContainingText:kErrorPageText]; -} - - (NSError*)waitForStaticHTMLViewContainingText:(NSString*)text { bool hasStaticView = WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^{ return chrome_test_util::StaticHtmlViewContainingText( @@ -616,33 +634,6 @@ chrome_test_util::InjectBookmarkOnFakeSyncServer(URL, title); } -- (NSError*)waitForTypedURL:(const GURL&)URL - expectPresent:(BOOL)expectPresent - timeout:(NSTimeInterval)timeout { - __block NSError* error = nil; - ConditionBlock condition = ^{ - NSError* __autoreleasing tempError = error; - BOOL success = chrome_test_util::IsTypedUrlPresentOnClient( - URL, expectPresent, &tempError); - error = tempError; - DCHECK(success || error); - return !!success; - }; - bool success = WaitUntilConditionOrTimeout(timeout, condition); - if (error != nil) { - return nil; - } - if (!success) { - return testing::NSErrorWithLocalizedDescription( - @"Error occurred during typed URL verification."); - } - return nil; -} - -- (void)deleteTypedURL:(const GURL&)URL { - chrome_test_util::DeleteTypedUrlFromClient(URL); -} - @end #endif // defined(CHROME_EARL_GREY_1)
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h index a3b348fb..21c99708 100644 --- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h +++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
@@ -22,9 +22,9 @@ // operation failed. + (NSError*)clearBrowsingHistory; -// Loads |URL| in the current WebState with transition type +// Loads the URL |spec| in the current WebState with transition type // ui::PAGE_TRANSITION_TYPED and returns without waiting for the page to load. -+ (void)startLoadingURL:(NSString*)URL; ++ (void)startLoadingURL:(NSString*)spec; // If the current WebState is HTML content, will wait until the window ID is // injected. Returns YES if the injection is successful or if the WebState is @@ -217,6 +217,14 @@ // Adds typed URL into HistoryService. + (void)addHistoryServiceTypedURL:(NSString*)URL; +// Deletes typed URL from HistoryService. ++ (void)deleteHistoryServiceTypedURL:(NSString*)URL; + +// If the provided URL |spec| is either present or not present in HistoryService +// (depending on |expectPresent|), return YES. If the present status of |spec| +// is not what is expected, or there is an error, return NO. ++ (BOOL)isTypedURL:(NSString*)spec presentOnClient:(BOOL)expectPresent; + // Triggers a sync cycle for a |type|. + (void)triggerSyncCycleForType:(syncer::ModelType)type;
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm index 89bad5c..a6407b7 100644 --- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm +++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -42,8 +42,8 @@ @"Clearing browser history timed out"); } -+ (void)startLoadingURL:(NSString*)URL { - chrome_test_util::LoadUrl(GURL(base::SysNSStringToUTF8(URL))); ++ (void)startLoadingURL:(NSString*)spec { + chrome_test_util::LoadUrl(GURL(base::SysNSStringToUTF8(spec))); } + (BOOL)waitForWindowIDInjectionIfNeeded { @@ -311,6 +311,19 @@ chrome_test_util::AddTypedURLOnClient(GURL(base::SysNSStringToUTF8(URL))); } ++ (void)deleteHistoryServiceTypedURL:(NSString*)URL { + chrome_test_util::DeleteTypedUrlFromClient( + GURL(base::SysNSStringToUTF8(URL))); +} + ++ (BOOL)isTypedURL:(NSString*)spec presentOnClient:(BOOL)expectPresent { + NSError* error = nil; + GURL URL(base::SysNSStringToUTF8(spec)); + BOOL success = + chrome_test_util::IsTypedUrlPresentOnClient(URL, expectPresent, &error); + return success && !error; +} + + (void)triggerSyncCycleForType:(syncer::ModelType)type { chrome_test_util::TriggerSyncCycle(type); }
diff --git a/ios/chrome/test/earl_grey2/smoke_egtest.mm b/ios/chrome/test/earl_grey2/smoke_egtest.mm index 1d08e64..7817dbd 100644 --- a/ios/chrome/test/earl_grey2/smoke_egtest.mm +++ b/ios/chrome/test/earl_grey2/smoke_egtest.mm
@@ -179,4 +179,12 @@ actualResult); } +// Tests typed URL converted helpers in chrome_earl_grey.h. +- (void)testTypedURLHelpers { + const GURL mockURL("http://not-a-real-site.test/"); + + [ChromeEarlGrey addHistoryServiceTypedURL:mockURL]; + [ChromeEarlGrey deleteHistoryServiceTypedURL:mockURL]; +} + @end
diff --git a/ios/testing/earl_grey/BUILD.gn b/ios/testing/earl_grey/BUILD.gn index 7a5367b..22cf840 100644 --- a/ios/testing/earl_grey/BUILD.gn +++ b/ios/testing/earl_grey/BUILD.gn
@@ -15,6 +15,8 @@ ] sources = [ + "app_launch_manager.h", + "app_launch_manager.mm", "base_earl_grey_test_case.h", "base_earl_grey_test_case.mm", "base_eg_test_helper_impl.h", @@ -59,6 +61,8 @@ testonly = true sources = [ + "app_launch_manager.h", + "app_launch_manager.mm", "base_earl_grey_test_case.h", "base_earl_grey_test_case.mm", "base_eg_test_helper_impl.h",
diff --git a/ios/testing/earl_grey/app_launch_manager.h b/ios/testing/earl_grey/app_launch_manager.h new file mode 100644 index 0000000..a16a9d2 --- /dev/null +++ b/ios/testing/earl_grey/app_launch_manager.h
@@ -0,0 +1,32 @@ +// Copyright 2019 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 IOS_TESTING_EARL_GREY_APP_LAUNCH_MANAGER_H_ +#define IOS_TESTING_EARL_GREY_APP_LAUNCH_MANAGER_H_ + +#import <Foundation/Foundation.h> + +// Provides control of the single application-under-test to EarlGrey 2 tests. +@interface AppLaunchManager : NSObject + +// Returns the singleton instance of this class. ++ (AppLaunchManager*)sharedManager; + +- (instancetype)init NS_UNAVAILABLE; + +// Makes sure the app has been started with the appropriate |arguments|. +// In EG2, will launch the app if any of the following conditions are met: +// * The app is not running +// * The app is currently running with different arguments. +// * |forceRestart| is YES +// Otherwise, the app will be activated instead of (re)launched. +// Will wait until app is activated or launched, and fail the test if it +// fails to do so. +// In EG1, this method is a no-op. +- (void)ensureAppLaunchedWithArgs:(NSArray<NSString*>*)arguments + forceRestart:(BOOL)forceRestart; + +@end + +#endif // IOS_TESTING_EARL_GREY_APP_LAUNCH_MANAGER_H_ \ No newline at end of file
diff --git a/ios/testing/earl_grey/app_launch_manager.mm b/ios/testing/earl_grey/app_launch_manager.mm new file mode 100644 index 0000000..68a65b0 --- /dev/null +++ b/ios/testing/earl_grey/app_launch_manager.mm
@@ -0,0 +1,70 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/testing/earl_grey/app_launch_manager.h" + +#import <XCTest/XCTest.h> + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#if defined(CHROME_EARL_GREY_2) // avoid unused function warning in EG1 +namespace { +// Checks if two pairs of launch arguments are equivalent. +bool LaunchArgumentsAreEqual(NSArray<NSString*>* args1, + NSArray<NSString*>* args2) { + // isEqualToArray will only return true if both arrays are non-nil, + // so first check if both arrays are empty or nil + if (!args1.count && !args2.count) { + return true; + } + + return [args1 isEqualToArray:args2]; +} +} // namespace +#endif + +@interface AppLaunchManager () +@property(nonatomic) XCUIApplication* runningApplication; +@property(nonatomic) NSArray<NSString*>* currentLaunchArgs; +@end + +@implementation AppLaunchManager + ++ (AppLaunchManager*)sharedManager { + static AppLaunchManager* instance = nil; + static dispatch_once_t guard; + dispatch_once(&guard, ^{ + instance = [[AppLaunchManager alloc] initPrivate]; + }); + return instance; +} + +- (instancetype)initPrivate { + self = [super init]; + return self; +} + +- (void)ensureAppLaunchedWithArgs:(NSArray<NSString*>*)arguments + forceRestart:(BOOL)forceRestart { +#if defined(CHROME_EARL_GREY_2) + bool appNeedsLaunching = + forceRestart || !self.runningApplication || + !LaunchArgumentsAreEqual(arguments, self.currentLaunchArgs); + + if (!appNeedsLaunching) { + [self.runningApplication activate]; + return; + } + + XCUIApplication* application = [[XCUIApplication alloc] init]; + application.launchArguments = arguments; + + [application launch]; + self.runningApplication = application; + self.currentLaunchArgs = arguments; +#endif +} +@end
diff --git a/ios/testing/earl_grey/base_earl_grey_test_case.mm b/ios/testing/earl_grey/base_earl_grey_test_case.mm index fe496a9..cb4f8a3 100644 --- a/ios/testing/earl_grey/base_earl_grey_test_case.mm +++ b/ios/testing/earl_grey/base_earl_grey_test_case.mm
@@ -7,6 +7,7 @@ #import <UIKit/UIKit.h> #import <objc/runtime.h> +#import "ios/testing/earl_grey/app_launch_manager.h" #import "ios/testing/earl_grey/coverage_utils.h" #import "ios/testing/earl_grey/earl_grey_test.h" @@ -41,11 +42,8 @@ } - (void)launchAppForTestMethod { - static dispatch_once_t launchAppToken; - dispatch_once(&launchAppToken, ^{ - XCUIApplication* application = [[XCUIApplication alloc] init]; - [application launch]; - }); + [[AppLaunchManager sharedManager] ensureAppLaunchedWithArgs:nil + forceRestart:false]; } // Prevents tests inheriting from this class from putting logic in +setUp.
diff --git a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java index 15415a50..a2ddfd5 100644 --- a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java +++ b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
@@ -53,7 +53,7 @@ /** * A wrapper of the android MediaDrm class. Each MediaDrmBridge manages multiple sessions for - * MediaCodecAudioDecoders, and AndroidVideoDecodeAccelerators or MediaCodecVideoDecoders. + * MediaCodecAudioDecoders or MediaCodecVideoDecoders. */ @JNINamespace("media") @MainDex
diff --git a/media/base/android/media_codec_loop.h b/media/base/android/media_codec_loop.h index 4791db0..eded6bb 100644 --- a/media/base/android/media_codec_loop.h +++ b/media/base/android/media_codec_loop.h
@@ -29,7 +29,7 @@ // One MediaCodecLoop instance owns a single MediaCodec(Bridge) instance, and // drives it to perform decoding in conjunction with a MediaCodecLoop::Client. // The Client provides the input data and consumes the output data. A typical -// example is AndroidVideoDecodeAccelerator. +// example is MediaCodecAudioDecoder. // Implementation notes. //
diff --git a/media/base/bitstream_buffer.h b/media/base/bitstream_buffer.h index 8a72eceb9..343ca070 100644 --- a/media/base/bitstream_buffer.h +++ b/media/base/bitstream_buffer.h
@@ -119,9 +119,7 @@ size_t size_; off_t offset_; - // This is only set when necessary. For example, AndroidVideoDecodeAccelerator - // needs the timestamp because the underlying decoder may require it to - // determine the output order. + // Note: Not set by all clients. base::TimeDelta presentation_timestamp_; // Note that BitstreamBuffer uses the settings in Audio/VideoDecoderConfig
diff --git a/media/base/key_systems.cc b/media/base/key_systems.cc index e6d3a5bd..a85513f 100644 --- a/media/base/key_systems.cc +++ b/media/base/key_systems.cc
@@ -103,13 +103,14 @@ case kCodecHEVC: return EME_CODEC_HEVC; case kCodecDolbyVision: - // Only profiles 0, 4, 5 and 7 are valid. Profile 0 is encoded based on - // AVC while profile 4, 5 and 7 are based on HEVC. - if (profile == DOLBYVISION_PROFILE0) { + // Only profiles 0, 4, 5, 7, 8, 9 are valid. Profile 0 and 9 are encoded + // based on AVC while profile 4, 5, 7 and 8 are based on HEVC. + if (profile == DOLBYVISION_PROFILE0 || profile == DOLBYVISION_PROFILE9) { return EME_CODEC_DOLBY_VISION_AVC; } else if (profile == DOLBYVISION_PROFILE4 || profile == DOLBYVISION_PROFILE5 || - profile == DOLBYVISION_PROFILE7) { + profile == DOLBYVISION_PROFILE7 || + profile == DOLBYVISION_PROFILE8) { return EME_CODEC_DOLBY_VISION_HEVC; } else { return EME_CODEC_NONE;
diff --git a/media/base/test_helpers.cc b/media/base/test_helpers.cc index 80a26ca..5fa7bae4 100644 --- a/media/base/test_helpers.cc +++ b/media/base/test_helpers.cc
@@ -160,7 +160,7 @@ case kCodecHEVC: return HEVCPROFILE_MIN; case kCodecDolbyVision: - return DOLBYVISION_MIN; + return DOLBYVISION_PROFILE0; case kCodecAV1: return AV1PROFILE_MIN; }
diff --git a/media/base/video_codecs.cc b/media/base/video_codecs.cc index c0f1835..5665b62 100644 --- a/media/base/video_codecs.cc +++ b/media/base/video_codecs.cc
@@ -92,6 +92,10 @@ return "dolby vision profile 5"; case DOLBYVISION_PROFILE7: return "dolby vision profile 7"; + case DOLBYVISION_PROFILE8: + return "dolby vision profile 8"; + case DOLBYVISION_PROFILE9: + return "dolby vision profile 9"; case THEORAPROFILE_ANY: return "theora"; case AV1PROFILE_PROFILE_MAIN: @@ -756,26 +760,31 @@ // Profile string should be two digits. unsigned profile_id = 0; if (elem[1].size() != 2 || !base::StringToUint(elem[1], &profile_id) || - profile_id > 7) { + profile_id > 9) { DVLOG(4) << __func__ << ": invalid format or profile_id=" << elem[1]; return false; } - // Only profiles 0, 4, 5 and 7 are valid. Profile 0 is encoded based on AVC - // while profile 4, 5 and 7 are based on HEVC. + // Only profiles 0, 4, 5, 7, 8 and 9 are valid. Profile 0 and 9 are encoded + // based on AVC while profile 4, 5, 7 and 8 are based on HEVC. switch (profile_id) { case 0: + case 9: if (!IsDolbyVisionAVCCodecId(codec_id)) { DVLOG(4) << __func__ << ": codec id is mismatched with profile_id=" << profile_id; return false; } - *profile = DOLBYVISION_PROFILE0; + if (profile_id == 0) + *profile = DOLBYVISION_PROFILE0; + else if (profile_id == 9) + *profile = DOLBYVISION_PROFILE9; break; #if BUILDFLAG(ENABLE_HEVC_DEMUXING) case 4: case 5: case 7: + case 8: if (!IsDolbyVisionHEVCCodecId(codec_id)) { DVLOG(4) << __func__ << ": codec id is mismatched with profile_id=" << profile_id; @@ -787,6 +796,8 @@ *profile = DOLBYVISION_PROFILE5; else if (profile_id == 7) *profile = DOLBYVISION_PROFILE7; + else if (profile_id == 8) + *profile = DOLBYVISION_PROFILE8; break; #endif default:
diff --git a/media/base/video_codecs.h b/media/base/video_codecs.h index 119634b..79c5daf 100644 --- a/media/base/video_codecs.h +++ b/media/base/video_codecs.h
@@ -79,12 +79,10 @@ HEVCPROFILE_MAIN10 = 17, HEVCPROFILE_MAIN_STILL_PICTURE = 18, HEVCPROFILE_MAX = HEVCPROFILE_MAIN_STILL_PICTURE, - DOLBYVISION_MIN = 19, - DOLBYVISION_PROFILE0 = DOLBYVISION_MIN, + DOLBYVISION_PROFILE0 = 19, DOLBYVISION_PROFILE4 = 20, DOLBYVISION_PROFILE5 = 21, DOLBYVISION_PROFILE7 = 22, - DOLBYVISION_MAX = DOLBYVISION_PROFILE7, THEORAPROFILE_MIN = 23, THEORAPROFILE_ANY = THEORAPROFILE_MIN, THEORAPROFILE_MAX = THEORAPROFILE_ANY, @@ -93,7 +91,9 @@ AV1PROFILE_PROFILE_HIGH = 25, AV1PROFILE_PROFILE_PRO = 26, AV1PROFILE_MAX = AV1PROFILE_PROFILE_PRO, - VIDEO_CODEC_PROFILE_MAX = AV1PROFILE_PROFILE_PRO, + DOLBYVISION_PROFILE8 = 27, + DOLBYVISION_PROFILE9 = 28, + VIDEO_CODEC_PROFILE_MAX = DOLBYVISION_PROFILE9, }; struct CodecProfileLevel {
diff --git a/media/base/video_decoder_config.cc b/media/base/video_decoder_config.cc index 23aa265e..c16ce85 100644 --- a/media/base/video_decoder_config.cc +++ b/media/base/video_decoder_config.cc
@@ -46,6 +46,8 @@ case DOLBYVISION_PROFILE4: case DOLBYVISION_PROFILE5: case DOLBYVISION_PROFILE7: + case DOLBYVISION_PROFILE8: + case DOLBYVISION_PROFILE9: return kCodecDolbyVision; case THEORAPROFILE_ANY: return kCodecTheora;
diff --git a/media/cdm/BUILD.gn b/media/cdm/BUILD.gn index 091ca56..d49e488 100644 --- a/media/cdm/BUILD.gn +++ b/media/cdm/BUILD.gn
@@ -106,31 +106,6 @@ } } -config("cdm_manager_implementation") { - defines = [ "CDM_MANAGER_IMPLEMENTATION" ] -} - -# cdm_manager must not be a source_set() because CdmManager exposes a static -# singleton, shared by multiple component()s. -# -# TODO(xhwang): Remove this component once AVDA no longer depends on it. -component("cdm_manager") { - visibility = [ - "//media/gpu", - "//media/mojo/services", - ] - sources = [ - "cdm_manager.cc", - "cdm_manager.h", - "cdm_manager_export.h", - ] - configs += [ ":cdm_manager_implementation" ] - deps = [ - "//base", - "//media", - ] -} - static_library("cdm_paths") { sources = [ "cdm_paths.cc",
diff --git a/media/cdm/cdm_manager.cc b/media/cdm/cdm_manager.cc deleted file mode 100644 index 277333ca..0000000 --- a/media/cdm/cdm_manager.cc +++ /dev/null
@@ -1,43 +0,0 @@ -// 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 "media/cdm/cdm_manager.h" - -#include <utility> - -#include "base/logging.h" -#include "media/base/content_decryption_module.h" - -namespace media { - -CdmManager::CdmManager() = default; - -CdmManager::~CdmManager() = default; - -// static -CdmManager* CdmManager::GetInstance() { - static CdmManager* manager = new CdmManager(); - return manager; -} - -scoped_refptr<ContentDecryptionModule> CdmManager::GetCdm(int cdm_id) { - base::AutoLock lock(lock_); - auto iter = cdm_map_.find(cdm_id); - return iter == cdm_map_.end() ? nullptr : iter->second; -} - -void CdmManager::RegisterCdm(int cdm_id, - scoped_refptr<ContentDecryptionModule> cdm) { - base::AutoLock lock(lock_); - DCHECK(!cdm_map_.count(cdm_id)); - cdm_map_[cdm_id] = cdm; -} - -void CdmManager::UnregisterCdm(int cdm_id) { - base::AutoLock lock(lock_); - DCHECK(cdm_map_.count(cdm_id)); - cdm_map_.erase(cdm_id); -} - -} // namespace media
diff --git a/media/cdm/cdm_manager.h b/media/cdm/cdm_manager.h deleted file mode 100644 index 20d32b0..0000000 --- a/media/cdm/cdm_manager.h +++ /dev/null
@@ -1,52 +0,0 @@ -// 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 MEDIA_CDM_CDM_MANAGER_H_ -#define MEDIA_CDM_CDM_MANAGER_H_ - -#include <map> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "base/thread_annotations.h" -#include "media/base/media_export.h" -#include "media/cdm/cdm_manager_export.h" - -namespace media { - -class ContentDecryptionModule; - -// Provides a singleton registry of CDM instances. This is used to share -// ContentDecryptionModules between the MojoMediaService and -// AndroidVideoDecodeAccelerator, and should be removed along with AVDA in the -// future. (MojoCdmServiceContext serves the same purpose for Media Mojo -// services, but scoped to a single InterfaceFactory.) -class CDM_MANAGER_EXPORT CdmManager { - public: - CdmManager(); - ~CdmManager(); - - static CdmManager* GetInstance(); - - // Returns the CDM associated with |cdm_id|. Can be called on any thread. - scoped_refptr<ContentDecryptionModule> GetCdm(int cdm_id); - - // Registers the |cdm| for |cdm_id|. - void RegisterCdm(int cdm_id, scoped_refptr<ContentDecryptionModule> cdm); - - // Unregisters the CDM associated with |cdm_id|. - void UnregisterCdm(int cdm_id); - - private: - base::Lock lock_; - std::map<int, scoped_refptr<ContentDecryptionModule>> cdm_map_ - GUARDED_BY(lock_); - - DISALLOW_COPY_AND_ASSIGN(CdmManager); -}; - -} // namespace media - -#endif // MEDIA_CDM_CDM_MANAGER_H_
diff --git a/media/cdm/cdm_manager_export.h b/media/cdm/cdm_manager_export.h deleted file mode 100644 index fc65a72..0000000 --- a/media/cdm/cdm_manager_export.h +++ /dev/null
@@ -1,32 +0,0 @@ -// 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 MEDIA_CDM_CDM_MANAGER_EXPORT_H_ -#define MEDIA_CDM_CDM_MANAGER_EXPORT_H_ - -// Define CDM_MANAGER_EXPORT so that functionality implemented by the -// cdm_manager component can be exported to consumers. - -#if defined(COMPONENT_BUILD) -#if defined(WIN32) - -#if defined(CDM_MANAGER_IMPLEMENTATION) -#define CDM_MANAGER_EXPORT __declspec(dllexport) -#else -#define CDM_MANAGER_EXPORT __declspec(dllimport) -#endif // defined(CDM_MANAGER_IMPLEMENTATION) - -#else // defined(WIN32) -#if defined(CDM_MANAGER_IMPLEMENTATION) -#define CDM_MANAGER_EXPORT __attribute__((visibility("default"))) -#else -#define CDM_MANAGER_EXPORT -#endif -#endif - -#else // defined(COMPONENT_BUILD) -#define CDM_MANAGER_EXPORT -#endif - -#endif // MEDIA_BASE_CDM_MANAGER_EXPORT_H_
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc index f057b8e..19b1a1e1 100644 --- a/media/filters/gpu_video_decoder.cc +++ b/media/filters/gpu_video_decoder.cc
@@ -455,8 +455,6 @@ } memcpy(shared_memory->memory(), buffer->data(), size); - // AndroidVideoDecodeAccelerator needs the timestamp to output frames in - // presentation order. BitstreamBuffer bitstream_buffer( next_bitstream_buffer_id_, shared_memory->handle(), false /* read_only */, size, 0, buffer->timestamp());
diff --git a/media/formats/mp4/dolby_vision.cc b/media/formats/mp4/dolby_vision.cc index eac7831..9462dd4 100644 --- a/media/formats/mp4/dolby_vision.cc +++ b/media/formats/mp4/dolby_vision.cc
@@ -65,6 +65,12 @@ case 7: codec_profile = DOLBYVISION_PROFILE7; break; + case 8: + codec_profile = DOLBYVISION_PROFILE8; + break; + case 9: + codec_profile = DOLBYVISION_PROFILE9; + break; default: DVLOG(2) << "Deprecated or invalid Dolby Vision profile:" << static_cast<int>(dv_profile);
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn index b03efdd..e679efd 100644 --- a/media/gpu/BUILD.gn +++ b/media/gpu/BUILD.gn
@@ -91,21 +91,12 @@ if (is_android) { sources += [ - "android/android_video_decode_accelerator.cc", - "android/android_video_decode_accelerator.h", "android/android_video_encode_accelerator.cc", "android/android_video_encode_accelerator.h", "android/android_video_surface_chooser.cc", "android/android_video_surface_chooser.h", "android/android_video_surface_chooser_impl.cc", "android/android_video_surface_chooser_impl.h", - "android/avda_codec_image.cc", - "android/avda_codec_image.h", - "android/avda_picture_buffer_manager.cc", - "android/avda_picture_buffer_manager.h", - "android/avda_shared_state.cc", - "android/avda_shared_state.h", - "android/avda_state_provider.h", "android/avda_surface_bundle.cc", "android/avda_surface_bundle.h", "android/codec_allocator.cc", @@ -150,13 +141,6 @@ if (enable_vulkan) { deps += [ "//gpu/vulkan:vulkan" ] } - - # TODO(crbug.com/789435): This is needed for AVDA to access the CDM - # directly. Remove this dependency after VDAs are also running as part of - # the mojo media service. See http://crbug.com/522298 - if (mojo_media_host == "gpu") { - deps += [ "//media/cdm:cdm_manager" ] - } } if (use_v4l2_codec) { @@ -455,7 +439,6 @@ if (is_android) { testonly = true sources = [ - "android/android_video_decode_accelerator_unittest.cc", "android/android_video_surface_chooser_impl_unittest.cc", "android/codec_allocator_unittest.cc", "android/codec_image_group_unittest.cc",
diff --git a/media/gpu/android/android_video_decode_accelerator.cc b/media/gpu/android/android_video_decode_accelerator.cc deleted file mode 100644 index 934c4a7..0000000 --- a/media/gpu/android/android_video_decode_accelerator.cc +++ /dev/null
@@ -1,1836 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/gpu/android/android_video_decode_accelerator.h" - -#include <stddef.h> - -#include <memory> - -#include "base/android/build_info.h" -#include "base/auto_reset.h" -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback_helpers.h" -#include "base/command_line.h" -#include "base/containers/queue.h" -#include "base/logging.h" -#include "base/metrics/histogram_macros.h" -#include "base/system/sys_info.h" -#include "base/task_runner_util.h" -#include "base/threading/thread.h" -#include "base/threading/thread_checker.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/trace_event/trace_event.h" -#include "gpu/command_buffer/service/gles2_cmd_decoder.h" -#include "gpu/command_buffer/service/mailbox_manager.h" -#include "gpu/ipc/service/gpu_channel.h" -#include "media/base/android/media_codec_bridge_impl.h" -#include "media/base/android/media_codec_util.h" -#include "media/base/bind_to_current_loop.h" -#include "media/base/bitstream_buffer.h" -#include "media/base/limits.h" -#include "media/base/media.h" -#include "media/base/media_switches.h" -#include "media/base/media_util.h" -#include "media/base/timestamp_constants.h" -#include "media/base/unaligned_shared_memory.h" -#include "media/base/video_decoder_config.h" -#include "media/gpu/android/android_video_surface_chooser_impl.h" -#include "media/gpu/android/avda_picture_buffer_manager.h" -#include "media/gpu/android/device_info.h" -#include "media/gpu/android/promotion_hint_aggregator_impl.h" -#include "media/media_buildflags.h" -#include "media/mojo/buildflags.h" -#include "media/video/picture.h" -#include "services/service_manager/public/cpp/service_context_ref.h" -#include "ui/gl/android/scoped_java_surface.h" -#include "ui/gl/android/surface_texture.h" -#include "ui/gl/gl_bindings.h" - -#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) -#include "media/cdm/cdm_manager.h" // nogncheck -#endif - -#define NOTIFY_ERROR(error_code, error_message) \ - do { \ - DLOG(ERROR) << error_message; \ - NotifyError(VideoDecodeAccelerator::error_code); \ - } while (0) - -namespace media { - -namespace { - -enum { kNumPictureBuffers = limits::kMaxVideoFrames + 1 }; - -// Max number of bitstreams notified to the client with -// NotifyEndOfBitstreamBuffer() before getting output from the bitstream. -enum { kMaxBitstreamsNotifiedInAdvance = 32 }; - -// MediaCodec is only guaranteed to support baseline, but some devices may -// support others. Advertise support for all H264 profiles and let the -// MediaCodec fail when decoding if it's not actually supported. It's assumed -// that consumers won't have software fallback for H264 on Android anyway. -#if BUILDFLAG(USE_PROPRIETARY_CODECS) -constexpr VideoCodecProfile kSupportedH264Profiles[] = { - H264PROFILE_BASELINE, - H264PROFILE_MAIN, - H264PROFILE_EXTENDED, - H264PROFILE_HIGH, - H264PROFILE_HIGH10PROFILE, - H264PROFILE_HIGH422PROFILE, - H264PROFILE_HIGH444PREDICTIVEPROFILE, - H264PROFILE_SCALABLEBASELINE, - H264PROFILE_SCALABLEHIGH, - H264PROFILE_STEREOHIGH, - H264PROFILE_MULTIVIEWHIGH}; - -#if BUILDFLAG(ENABLE_HEVC_DEMUXING) -constexpr VideoCodecProfile kSupportedHevcProfiles[] = {HEVCPROFILE_MAIN, - HEVCPROFILE_MAIN10}; -#endif -#endif - -// Because MediaCodec is thread-hostile (must be poked on a single thread) and -// has no callback mechanism (b/11990118), we must drive it by polling for -// complete frames (and available input buffers, when the codec is fully -// saturated). This function defines the polling delay. The value used is an -// arbitrary choice that trades off CPU utilization (spinning) against latency. -// Mirrors android_video_encode_accelerator.cc:EncodePollDelay(). -// -// An alternative to this polling scheme could be to dedicate a new thread -// (instead of using the ChildThread) to run the MediaCodec, and make that -// thread use the timeout-based flavor of MediaCodec's dequeue methods when it -// believes the codec should complete "soon" (e.g. waiting for an input -// buffer, or waiting for a picture when it knows enough complete input -// pictures have been fed to saturate any internal buffering). This is -// speculative and it's unclear that this would be a win (nor that there's a -// reasonably device-agnostic way to fill in the "believes" above). -constexpr base::TimeDelta DecodePollDelay = - base::TimeDelta::FromMilliseconds(10); - -constexpr base::TimeDelta NoWaitTimeOut = base::TimeDelta::FromMicroseconds(0); - -constexpr base::TimeDelta IdleTimerTimeOut = base::TimeDelta::FromSeconds(1); - -// On low end devices (< KitKat is always low-end due to buggy MediaCodec), -// defer the surface creation until the codec is actually used if we know no -// software fallback exists. -bool ShouldDeferSurfaceCreation(CodecAllocator* codec_allocator, - const OverlayInfo& overlay_info, - VideoCodec codec, - DeviceInfo* device_info) { - // TODO(liberato): We might still want to defer if we've got a routing - // token. It depends on whether we want to use it right away or not. - if (overlay_info.HasValidRoutingToken()) - return false; - - return codec == kCodecH264 && codec_allocator->IsAnyRegisteredAVDA() && - device_info->SdkVersion() <= base::android::SDK_VERSION_JELLY_BEAN_MR2; -} - -bool HasValidCdm(int cdm_id) { -#if !BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) - return false; -#else - auto cdm = CdmManager::GetInstance()->GetCdm(cdm_id); - if (!cdm) { - // This could happen during the destruction of the media element and the CDM - // and due to IPC CDM could be destroyed before the decoder. - DVLOG(1) << "CDM not available."; - return false; - } - - auto* cdm_context = cdm->GetCdmContext(); - auto* media_crypto_context = - cdm_context ? cdm_context->GetMediaCryptoContext() : nullptr; - // This could happen if the CDM is not MediaDrmBridge, which could happen in - // test cases. - if (!media_crypto_context) { - DVLOG(1) << "MediaCryptoContext not available."; - return false; - } - - return true; -#endif -} - -} // namespace - -// AVDAManager manages a RepeatingTimer so that AVDAs can get a regular callback -// to DoIOTask(). -class AVDAManager { - public: - AVDAManager() {} - - // Request periodic callback of |avda|->DoIOTask(). Does nothing if the - // instance is already registered and the timer started. The first request - // will start the repeating timer on an interval of DecodePollDelay. - void StartTimer(AndroidVideoDecodeAccelerator* avda) { - DCHECK(thread_checker_.CalledOnValidThread()); - - timer_avda_instances_.insert(avda); - - // If the timer is running, StopTimer() might have been called earlier, if - // so remove the instance from the pending erasures. - if (timer_running_) - pending_erase_.erase(avda); - - if (io_timer_.IsRunning()) - return; - io_timer_.Start(FROM_HERE, DecodePollDelay, this, &AVDAManager::RunTimer); - } - - // Stop callbacks to |avda|->DoIOTask(). Does nothing if the instance is not - // registered. If there are no instances left, the repeating timer will be - // stopped. - void StopTimer(AndroidVideoDecodeAccelerator* avda) { - DCHECK(thread_checker_.CalledOnValidThread()); - - // If the timer is running, defer erasures to avoid iterator invalidation. - if (timer_running_) { - pending_erase_.insert(avda); - return; - } - - timer_avda_instances_.erase(avda); - if (timer_avda_instances_.empty()) - io_timer_.Stop(); - } - - private: - ~AVDAManager() = delete; - - void RunTimer() { - { - // Call out to all AVDA instances, some of which may attempt to remove - // themselves from the list during this operation; those removals will be - // deferred until after all iterations are complete. - base::AutoReset<bool> scoper(&timer_running_, true); - for (auto* avda : timer_avda_instances_) - avda->DoIOTask(false); - } - - // Take care of any deferred erasures. - for (auto* avda : pending_erase_) - StopTimer(avda); - pending_erase_.clear(); - - // TODO(dalecurtis): We may want to consider chunking this if task execution - // takes too long for the combined timer. - } - - // All AVDA instances that would like us to poll DoIOTask. - std::set<AndroidVideoDecodeAccelerator*> timer_avda_instances_; - - // Since we can't delete while iterating when using a set, defer erasure until - // after iteration complete. - bool timer_running_ = false; - std::set<AndroidVideoDecodeAccelerator*> pending_erase_; - - // Repeating timer responsible for draining pending IO to the codecs. - base::RepeatingTimer io_timer_; - - base::ThreadChecker thread_checker_; - - DISALLOW_COPY_AND_ASSIGN(AVDAManager); -}; - -static AVDAManager* GetManager() { - static AVDAManager* manager = new AVDAManager(); - return manager; -} - -AndroidVideoDecodeAccelerator::BitstreamRecord::BitstreamRecord( - BitstreamBuffer bitstream_buffer) - : buffer(std::move(bitstream_buffer)) { - if (buffer.id() != -1) { - memory.reset( - new UnalignedSharedMemory(buffer.TakeRegion(), buffer.size(), true)); - } -} - -AndroidVideoDecodeAccelerator::BitstreamRecord::BitstreamRecord( - BitstreamRecord&& other) = default; - -AndroidVideoDecodeAccelerator::BitstreamRecord::~BitstreamRecord() {} - -AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator( - CodecAllocator* codec_allocator, - std::unique_ptr<AndroidVideoSurfaceChooser> surface_chooser, - const MakeGLContextCurrentCallback& make_context_current_cb, - const GetContextGroupCallback& get_context_group_cb, - const AndroidOverlayMojoFactoryCB& overlay_factory_cb, - const CreateAbstractTextureCallback& create_abstract_texture_cb, - DeviceInfo* device_info) - : client_(nullptr), - codec_allocator_(codec_allocator), - make_context_current_cb_(make_context_current_cb), - get_context_group_cb_(get_context_group_cb), - state_(BEFORE_OVERLAY_INIT), - picturebuffers_requested_(false), - picture_buffer_manager_(this), - media_crypto_context_(nullptr), - cdm_registration_id_(0), - pending_input_buf_index_(-1), - during_initialize_(false), - deferred_initialization_pending_(false), - codec_needs_reset_(false), - defer_surface_creation_(false), - surface_chooser_helper_( - std::move(surface_chooser), - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kForceVideoOverlays), - base::FeatureList::IsEnabled(media::kUseAndroidOverlayAggressively), - false /* always_use_texture_owner */), - device_info_(device_info), - force_defer_surface_creation_for_testing_(false), - force_allow_software_decoding_for_testing_(false), - overlay_factory_cb_(overlay_factory_cb), - create_abstract_texture_cb_(create_abstract_texture_cb), - weak_this_factory_(this) {} - -AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() { - DCHECK(thread_checker_.CalledOnValidThread()); - GetManager()->StopTimer(this); - codec_allocator_->StopThread(this); - -#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) - if (!media_crypto_context_) - return; - - // Cancel previously registered callback (if any). - media_crypto_context_->SetMediaCryptoReadyCB( - MediaCryptoContext::MediaCryptoReadyCB()); - - if (cdm_registration_id_) - media_crypto_context_->UnregisterPlayer(cdm_registration_id_); -#endif // BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) -} - -bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, - Client* client) { - DVLOG(1) << __func__ << ": " << config.AsHumanReadableString(); - TRACE_EVENT0("media", "AVDA::Initialize"); - DCHECK(!media_codec_); - DCHECK(thread_checker_.CalledOnValidThread()); - base::AutoReset<bool> scoper(&during_initialize_, true); - - if (!make_context_current_cb_ || !get_context_group_cb_) { - DLOG(ERROR) << "GL callbacks are required for this VDA"; - return false; - } - - if (config.output_mode != Config::OutputMode::ALLOCATE) { - DLOG(ERROR) << "Only ALLOCATE OutputMode is supported by this VDA"; - return false; - } - - DCHECK(client); - client_ = client; - config_ = config; - codec_config_ = new CodecConfig(); - codec_config_->codec = VideoCodecProfileToVideoCodec(config.profile); - codec_config_->initial_expected_coded_size = - config.initial_expected_coded_size; - - switch (codec_config_->codec) { - case kCodecVP8: - case kCodecVP9: - break; - -#if BUILDFLAG(USE_PROPRIETARY_CODECS) - case kCodecH264: - codec_config_->csd0 = config.sps; - codec_config_->csd1 = config.pps; - break; -#if BUILDFLAG(ENABLE_HEVC_DEMUXING) - case kCodecHEVC: - break; -#endif -#endif - default: - DLOG(ERROR) << "Unsupported profile: " << GetProfileName(config.profile); - return false; - } - - codec_config_->software_codec_forbidden = - IsMediaCodecSoftwareDecodingForbidden(); - - codec_config_->container_color_space = config.container_color_space; - codec_config_->hdr_metadata = config.hdr_metadata; - - // Only use MediaCodec for VP8/9 if it's likely backed by hardware - // or if the stream is encrypted. - if (IsMediaCodecSoftwareDecodingForbidden() && - MediaCodecUtil::IsKnownUnaccelerated(codec_config_->codec, - MediaCodecDirection::DECODER)) { - DVLOG(1) << "Initialization failed: " << GetCodecName(codec_config_->codec) - << " is not hardware accelerated"; - return false; - } - - auto* context_group = get_context_group_cb_.Run(); - if (!context_group) { - DLOG(ERROR) << "Failed to get context group."; - return false; - } - - // We signaled that we support deferred initialization, so see if the client - // does also. - deferred_initialization_pending_ = config.is_deferred_initialization_allowed; - - // If we're low on resources, we may decide to defer creation of the surface - // until the codec is actually used. - if (force_defer_surface_creation_for_testing_ || - ShouldDeferSurfaceCreation(codec_allocator_, config_.overlay_info, - codec_config_->codec, device_info_)) { - // We should never be here if a SurfaceView is required. - // TODO(liberato): This really isn't true with AndroidOverlay. - defer_surface_creation_ = true; - } - - codec_allocator_->StartThread(this); - - // If has valid CDM, start by initializing the CDM, even for clear stream. - if (HasValidCdm(config_.cdm_id) && deferred_initialization_pending_) { - InitializeCdm(); - return state_ != ERROR; - } - - // Cannot handle encrypted stream without valid CDM. - if (config_.is_encrypted()) { - DLOG(ERROR) << "Deferred initialization must be used for encrypted streams"; - return false; - } - - StartSurfaceChooser(); - - // Fail / complete / defer initialization. - return state_ != ERROR; -} - -void AndroidVideoDecodeAccelerator::StartSurfaceChooser() { - DCHECK_EQ(state_, BEFORE_OVERLAY_INIT); - - // If we're trying to defer surface creation, then don't notify the chooser - // that it may start getting surfaces yet. We'll do that later. - if (defer_surface_creation_) { - if (deferred_initialization_pending_) - NotifyInitializationSucceeded(); - return; - } - - surface_chooser_helper_.SetIsFullscreen(config_.overlay_info.is_fullscreen); - - surface_chooser_helper_.chooser()->SetClientCallbacks( - base::Bind(&AndroidVideoDecodeAccelerator::OnSurfaceTransition, - weak_this_factory_.GetWeakPtr()), - base::Bind(&AndroidVideoDecodeAccelerator::OnSurfaceTransition, - weak_this_factory_.GetWeakPtr(), nullptr)); - - // Handle the sync path, which must use TextureOwner anyway. Note that we - // check both |during_initialize_| and |deferred_initialization_pending_|, - // since we might get here during deferred surface creation. In that case, - // Decode will call us (after clearing |defer_surface_creation_|), but - // deferred init will have already been signaled optimistically as success. - // - // Also note that we might choose to defer surface creation for the sync path, - // which won't get here. We'll exit above, successfully, during init, and - // will fall through to the below when Decode calls us back. That's okay. - // We only handle this case specially since |surface_chooser_| is allowed to - // post callbacks to us. Here, we guarantee that the sync case is actually - // resolved synchronously. The only exception will be if we need to defer - // surface creation for other reasons, in which case the sync path with just - // signal success optimistically. - if (during_initialize_ && !deferred_initialization_pending_) { - DCHECK(!config_.overlay_info.HasValidRoutingToken()); - // Note that we might still send feedback to |surface_chooser_|, which might - // call us back. However, it will only ever tell us to use TextureOwner, - // since we have no overlay factory anyway. - OnSurfaceTransition(nullptr); - return; - } - - // If we have a surface, then notify |surface_chooser_| about it. If we were - // told not to use an overlay (kNoSurfaceID or a null routing token), then we - // leave the factory blank. - AndroidOverlayFactoryCB factory; - if (config_.overlay_info.HasValidRoutingToken() && overlay_factory_cb_) { - factory = base::BindRepeating(overlay_factory_cb_, - *config_.overlay_info.routing_token); - } - - // Notify |surface_chooser_| that we've started. This guarantees that we'll - // get a callback. It might not be a synchronous callback, but we're not in - // the synchronous case. It will be soon, though. For pre-M, we rely on the - // fact that |surface_chooser_| won't tell us to use a TextureOwner while - // waiting for an overlay to become ready, for example. - surface_chooser_helper_.UpdateChooserState(std::move(factory)); -} - -void AndroidVideoDecodeAccelerator::OnSurfaceTransition( - std::unique_ptr<AndroidOverlay> overlay) { - if (overlay) { - overlay->AddSurfaceDestroyedCallback(base::Bind( - &AndroidVideoDecodeAccelerator::OnStopUsingOverlayImmediately, - weak_this_factory_.GetWeakPtr())); - } - - // If we're waiting for a surface (e.g., during startup), then proceed - // immediately. Otherwise, wait for Dequeue to handle it. This can probably - // be merged with UpdateSurface. - if (state_ == BEFORE_OVERLAY_INIT) { - DCHECK(!incoming_overlay_); - incoming_bundle_ = new AVDASurfaceBundle(std::move(overlay)); - InitializePictureBufferManager(); - return; - } - - // If, for some reason, |surface_chooser_| decides that we really should - // change our output surface pre-M, ignore it. For example, if the - // compositor tells us that it can't use an overlay, well, there's not much - // that we can do here unless we start falling forward to keyframes. - if (!device_info_->IsSetOutputSurfaceSupported()) - return; - - // If we're using a TextureOwner and are told to switch to one, then just - // do nothing. |surface_chooser_| doesn't really know if we've switched to - // TextureOwner or not. Note that it can't ask us to switch to the same - // overlay we're using, since it's unique_ptr. - if (!overlay && codec_config_->surface_bundle && - !codec_config_->surface_bundle->overlay) { - // Also stop transitioning to an overlay, if we were doing so. - incoming_overlay_.reset(); - return; - } - - incoming_overlay_ = std::move(overlay); -} - -void AndroidVideoDecodeAccelerator::InitializePictureBufferManager() { - DCHECK(!defer_surface_creation_); - DCHECK(incoming_bundle_); - - if (!make_context_current_cb_.Run()) { - NOTIFY_ERROR(PLATFORM_FAILURE, - "Failed to make this decoder's GL context current"); - incoming_bundle_ = nullptr; - return; - } - - // Move |incoming_bundle_| to |codec_config_|. Our caller must set up an - // incoming bundle properly, since we don't want to accidentally overwrite - // |surface_bundle| for a codec that's being released elsewhere. - // TODO(liberato): it doesn't make sense anymore for the PictureBufferManager - // to create the texture owner. We can probably make an overlay impl out - // of it, and provide the texture owner to |picture_buffer_manager_|. - if (!picture_buffer_manager_.Initialize(incoming_bundle_)) { - NOTIFY_ERROR(PLATFORM_FAILURE, "Could not allocate texture owner"); - incoming_bundle_ = nullptr; - return; - } - - // If we have a media codec, then SetSurface. If that doesn't work, then we - // do not try to allocate a new codec; we might not be at a keyframe, etc. - // If we get here with a codec, then we must setSurface. - if (media_codec_) { - // TODO(liberato): fail on api check? - if (!media_codec_->SetSurface(incoming_bundle_->GetJavaSurface())) { - NOTIFY_ERROR(PLATFORM_FAILURE, "MediaCodec failed to switch surfaces."); - // We're not going to use |incoming_bundle_|. - } else { - // We've switched surfaces, so replace |surface_bundle|. - codec_config_->surface_bundle = incoming_bundle_; - // We could be in BEFORE_OVERLAY_INIT, but we're not anymore. - state_ = NO_ERROR; - } - incoming_bundle_ = nullptr; - CacheFrameInformation(); - return; - } - - // We're going to create a codec with |incoming_bundle_|. It might fail, but - // either way, we're done with any previous bundle. Note that, since we - // never get here after init (i.e., we never change surfaces without using - // SetSurface), there shouldn't be any previous bundle. However, this is the - // right thing to do even if we can switch. - codec_config_->surface_bundle = incoming_bundle_; - incoming_bundle_ = nullptr; - CacheFrameInformation(); - - // If the client doesn't support deferred initialization (WebRTC), then we - // should complete it now and return a meaningful result. Note that it would - // be nice if we didn't have to worry about starting codec configuration at - // all (::Initialize or the wrapper can do it), but then they have to remember - // not to start codec config if we have to wait for the cdm. It's somewhat - // clearer for us to handle both cases. - // For this to be a case for sync configuration, we must be called from - // Initialize(), and the client must not want deferred init. Note that having - // |deferred_initialization_pending_| false by itself isn't enough; if we're - // deferring surface creation, then we'll finish deferred init before asking - // for the surface. We'll be called via Decode. - if (during_initialize_ && !deferred_initialization_pending_) { - ConfigureMediaCodecSynchronously(); - return; - } - - // In all other cases, we don't have to wait for the codec. - ConfigureMediaCodecAsynchronously(); -} - -void AndroidVideoDecodeAccelerator::DoIOTask(bool start_timer) { - DCHECK(thread_checker_.CalledOnValidThread()); - TRACE_EVENT0("media", "AVDA::DoIOTask"); - if (state_ == ERROR || state_ == WAITING_FOR_CODEC || - state_ == SURFACE_DESTROYED || state_ == BEFORE_OVERLAY_INIT) { - return; - } - - picture_buffer_manager_.MaybeRenderEarly(); - bool did_work = false, did_input = false, did_output = false; - do { - did_input = QueueInput(); - did_output = DequeueOutput(); - if (did_input || did_output) - did_work = true; - } while (did_input || did_output); - - ManageTimer(did_work || start_timer); -} - -bool AndroidVideoDecodeAccelerator::QueueInput() { - DCHECK(thread_checker_.CalledOnValidThread()); - TRACE_EVENT0("media", "AVDA::QueueInput"); - if (state_ == ERROR || state_ == WAITING_FOR_CODEC || - state_ == WAITING_FOR_KEY || state_ == BEFORE_OVERLAY_INIT) { - return false; - } - if (bitstreams_notified_in_advance_.size() > kMaxBitstreamsNotifiedInAdvance) - return false; - if (pending_bitstream_records_.empty()) - return false; - - int input_buf_index = pending_input_buf_index_; - - // Do not dequeue a new input buffer if we failed with MEDIA_CODEC_NO_KEY. - // That status does not return this buffer back to the pool of - // available input buffers. We have to reuse it in QueueSecureInputBuffer(). - if (input_buf_index == -1) { - MediaCodecStatus status = - media_codec_->DequeueInputBuffer(NoWaitTimeOut, &input_buf_index); - switch (status) { - case MEDIA_CODEC_TRY_AGAIN_LATER: - return false; - case MEDIA_CODEC_ERROR: - NOTIFY_ERROR(PLATFORM_FAILURE, "DequeueInputBuffer failed"); - return false; - case MEDIA_CODEC_OK: - break; - default: - NOTREACHED(); - return false; - } - } - - DCHECK_NE(input_buf_index, -1); - - BitstreamBuffer* bitstream_buffer = - &pending_bitstream_records_.front().buffer; - - if (bitstream_buffer->id() == -1) { - pending_bitstream_records_.pop(); - TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", - pending_bitstream_records_.size()); - - media_codec_->QueueEOS(input_buf_index); - return true; - } - - std::unique_ptr<UnalignedSharedMemory> shm; - - if (pending_input_buf_index_ == -1) { - // When |pending_input_buf_index_| is not -1, the buffer is already dequeued - // from MediaCodec and filled with data. The buffer shared memory handle is - // held by the front pending bitstream record. - shm = std::move(pending_bitstream_records_.front().memory); - - if (!shm->MapAt(bitstream_buffer->offset(), bitstream_buffer->size())) { - NOTIFY_ERROR(UNREADABLE_INPUT, "UnalignedSharedMemory::Map() failed"); - return false; - } - } - - const base::TimeDelta presentation_timestamp = - bitstream_buffer->presentation_timestamp(); - DCHECK(presentation_timestamp != kNoTimestamp) - << "Bitstream buffers must have valid presentation timestamps"; - - // There may already be a bitstream buffer with this timestamp, e.g., VP9 alt - // ref frames, but it's OK to overwrite it because we only expect a single - // output frame to have that timestamp. AVDA clients only use the bitstream - // buffer id in the returned Pictures to map a bitstream buffer back to a - // timestamp on their side, so either one of the bitstream buffer ids will - // result in them finding the right timestamp. - bitstream_buffers_in_decoder_[presentation_timestamp] = - bitstream_buffer->id(); - - // Notice that |memory| will be null if we repeatedly enqueue the same buffer, - // this happens after MEDIA_CODEC_NO_KEY. - const uint8_t* memory = - shm ? static_cast<const uint8_t*>(shm->memory()) : nullptr; - const std::string& key_id = bitstream_buffer->key_id(); - const std::string& iv = bitstream_buffer->iv(); - const std::vector<SubsampleEntry>& subsamples = - bitstream_buffer->subsamples(); - - MediaCodecStatus status; - if (key_id.empty() || iv.empty()) { - status = media_codec_->QueueInputBuffer(input_buf_index, memory, - bitstream_buffer->size(), - presentation_timestamp); - } else { - // VDAs only support "cenc" encryption scheme. - status = media_codec_->QueueSecureInputBuffer( - input_buf_index, memory, bitstream_buffer->size(), key_id, iv, - subsamples, AesCtrEncryptionScheme(), presentation_timestamp); - } - - DVLOG(2) << __func__ - << ": Queue(Secure)InputBuffer: pts:" << presentation_timestamp - << " status:" << status; - - if (status == MEDIA_CODEC_NO_KEY) { - // Keep trying to enqueue the same input buffer. - // The buffer is owned by us (not the MediaCodec) and is filled with data. - DVLOG(1) << "QueueSecureInputBuffer failed: NO_KEY"; - pending_input_buf_index_ = input_buf_index; - state_ = WAITING_FOR_KEY; - return false; - } - - pending_input_buf_index_ = -1; - // Popping the pending record invalides |bitstream_buffer|. - int32_t pending_bitstream_buffer_id = bitstream_buffer->id(); - pending_bitstream_records_.pop(); - TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", - pending_bitstream_records_.size()); - // We should call NotifyEndOfBitstreamBuffer(), when no more decoded output - // will be returned from the bitstream buffer. However, MediaCodec API is - // not enough to guarantee it. - // So, here, we calls NotifyEndOfBitstreamBuffer() in advance in order to - // keep getting more bitstreams from the client, and throttle them by using - // |bitstreams_notified_in_advance_|. - // TODO(dwkang): check if there is a way to remove this workaround. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer, - weak_this_factory_.GetWeakPtr(), - pending_bitstream_buffer_id)); - bitstreams_notified_in_advance_.push_back(pending_bitstream_buffer_id); - - if (status != MEDIA_CODEC_OK) { - NOTIFY_ERROR(PLATFORM_FAILURE, "QueueInputBuffer failed:" << status); - return false; - } - - return true; -} - -bool AndroidVideoDecodeAccelerator::DequeueOutput() { - DCHECK(thread_checker_.CalledOnValidThread()); - TRACE_EVENT0("media", "AVDA::DequeueOutput"); - if (state_ == ERROR || state_ == WAITING_FOR_CODEC || - state_ == BEFORE_OVERLAY_INIT) { - return false; - } - // If we're draining for reset or destroy, then we don't need picture buffers - // since we won't send any decoded frames anyway. There might not be any, - // since the pipeline might not be sending them back and / or they don't - // exist anymore. From the pipeline's point of view, for Destroy at least, - // the VDA is already gone. - if (picturebuffers_requested_ && output_picture_buffers_.empty() && - !IsDrainingForResetOrDestroy()) { - return false; - } - if (!output_picture_buffers_.empty() && free_picture_ids_.empty() && - !IsDrainingForResetOrDestroy()) { - // Don't have any picture buffer to send. Need to wait. - return false; - } - - // If we're waiting to switch surfaces pause output release until we have all - // picture buffers returned. This is so we can ensure the right flags are set - // on the picture buffers returned to the client. - if (incoming_overlay_) { - if (picture_buffer_manager_.HasUnrenderedPictures()) - return false; - if (!UpdateSurface()) - return false; - - // UpdateSurface should fail if we've transitioned to the error state. - DCHECK(state_ == NO_ERROR); - } - - bool eos = false; - base::TimeDelta presentation_timestamp; - int32_t buf_index = 0; - do { - size_t offset = 0; - size_t size = 0; - - TRACE_EVENT_BEGIN0("media", "AVDA::DequeueOutput"); - MediaCodecStatus status = media_codec_->DequeueOutputBuffer( - NoWaitTimeOut, &buf_index, &offset, &size, &presentation_timestamp, - &eos, NULL); - TRACE_EVENT_END2("media", "AVDA::DequeueOutput", "status", status, - "presentation_timestamp (ms)", - presentation_timestamp.InMilliseconds()); - - switch (status) { - case MEDIA_CODEC_ERROR: - // Do not post an error if we are draining for reset and destroy. - // Instead, signal completion of the drain. - if (IsDrainingForResetOrDestroy()) { - DVLOG(1) << __func__ << ": error while draining"; - state_ = ERROR; - OnDrainCompleted(); - } else { - NOTIFY_ERROR(PLATFORM_FAILURE, "DequeueOutputBuffer failed."); - } - return false; - - case MEDIA_CODEC_TRY_AGAIN_LATER: - return false; - - case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: { - // An OUTPUT_FORMAT_CHANGED is not reported after flush() if the frame - // size does not change. Therefore we have to keep track on the format - // even if draining, unless we are draining for destroy. - if (drain_type_ == DRAIN_FOR_DESTROY) - return true; // ignore - - if (media_codec_->GetOutputSize(&size_) != MEDIA_CODEC_OK) { - NOTIFY_ERROR(PLATFORM_FAILURE, "GetOutputSize failed."); - return false; - } - - DVLOG(3) << __func__ - << " OUTPUT_FORMAT_CHANGED, new size: " << size_.ToString(); - - // Don't request picture buffers if we already have some. This avoids - // having to dismiss the existing buffers which may actively reference - // decoded images. Breaking their connection to the decoded image will - // cause rendering of black frames. Instead, we let the existing - // PictureBuffers live on and we simply update their size the next time - // they're attached to an image of the new resolution. See the - // size update in |SendDecodedFrameToClient| and https://crbug/587994. - if (output_picture_buffers_.empty() && !picturebuffers_requested_) { - picturebuffers_requested_ = true; - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce( - &AndroidVideoDecodeAccelerator::RequestPictureBuffers, - weak_this_factory_.GetWeakPtr())); - return false; - } - - return true; - } - - case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: - break; - - case MEDIA_CODEC_OK: - DCHECK_GE(buf_index, 0); - DVLOG(3) << __func__ << ": pts:" << presentation_timestamp - << " buf_index:" << buf_index << " offset:" << offset - << " size:" << size << " eos:" << eos; - break; - - default: - NOTREACHED(); - break; - } - } while (buf_index < 0); - - if (eos) { - OnDrainCompleted(); - return false; - } - - if (IsDrainingForResetOrDestroy()) { - media_codec_->ReleaseOutputBuffer(buf_index, false); - return true; - } - - if (!picturebuffers_requested_) { - // In 0.01% of playbacks MediaCodec returns a frame before FORMAT_CHANGED. - // Occurs on JB and M. (See the Media.AVDA.MissingFormatChanged histogram.) - media_codec_->ReleaseOutputBuffer(buf_index, false); - NOTIFY_ERROR(PLATFORM_FAILURE, "Dequeued buffers before FORMAT_CHANGED."); - return false; - } - - // Get the bitstream buffer id from the timestamp. - auto it = bitstream_buffers_in_decoder_.find(presentation_timestamp); - - if (it != bitstream_buffers_in_decoder_.end()) { - const int32_t bitstream_buffer_id = it->second; - bitstream_buffers_in_decoder_.erase(bitstream_buffers_in_decoder_.begin(), - ++it); - SendDecodedFrameToClient(buf_index, bitstream_buffer_id); - - // Removes ids former or equal than the id from decoder. Note that - // |bitstreams_notified_in_advance_| does not mean bitstream ids in decoder - // because of frame reordering issue. We just maintain this roughly and use - // it for throttling. - for (auto bitstream_it = bitstreams_notified_in_advance_.begin(); - bitstream_it != bitstreams_notified_in_advance_.end(); - ++bitstream_it) { - if (*bitstream_it == bitstream_buffer_id) { - bitstreams_notified_in_advance_.erase( - bitstreams_notified_in_advance_.begin(), ++bitstream_it); - break; - } - } - } else { - // Normally we assume that the decoder makes at most one output frame for - // each distinct input timestamp. However MediaCodecBridge uses timestamp - // correction and provides a non-decreasing timestamp sequence, which might - // result in timestamp duplicates. Discard the frame if we cannot get the - // corresponding buffer id. - DVLOG(3) << __func__ << ": Releasing buffer with unexpected PTS: " - << presentation_timestamp; - media_codec_->ReleaseOutputBuffer(buf_index, false); - } - - // We got a decoded frame, so try for another. - return true; -} - -void AndroidVideoDecodeAccelerator::SendDecodedFrameToClient( - int32_t codec_buffer_index, - int32_t bitstream_id) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK_NE(bitstream_id, -1); - DCHECK(!free_picture_ids_.empty()); - TRACE_EVENT0("media", "AVDA::SendDecodedFrameToClient"); - - if (!make_context_current_cb_.Run()) { - NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to make the GL context current."); - return; - } - - int32_t picture_buffer_id = free_picture_ids_.front(); - free_picture_ids_.pop(); - TRACE_COUNTER1("media", "AVDA::FreePictureIds", free_picture_ids_.size()); - - const auto it = output_picture_buffers_.find(picture_buffer_id); - if (it == output_picture_buffers_.end()) { - NOTIFY_ERROR(PLATFORM_FAILURE, - "Can't find PictureBuffer id: " << picture_buffer_id); - return; - } - - PictureBuffer& picture_buffer = it->second; - const bool size_changed = picture_buffer.size() != size_; - if (size_changed) - picture_buffer.set_size(size_); - - // Only ask for promotion hints if we can actually switch surfaces. - const bool want_promotion_hint = device_info_->IsSetOutputSurfaceSupported(); - const bool allow_overlay = picture_buffer_manager_.ArePicturesOverlayable(); - - // TODO(liberato): remove in M63, if FrameInformation is clearly working. - UMA_HISTOGRAM_BOOLEAN("Media.AVDA.FrameSentAsOverlay", allow_overlay); - - // Record the frame type that we're sending and some information about why. - UMA_HISTOGRAM_ENUMERATION( - "Media.AVDA.FrameInformation", cached_frame_information_, - static_cast<int>( - SurfaceChooserHelper::FrameInformation::FRAME_INFORMATION_MAX) + - 1); // PRESUBMIT_IGNORE_UMA_MAX - - // We unconditionally mark the picture as overlayable, even if - // |!allow_overlay|, if we want to get hints. It's required, else we won't - // get hints. - // TODO(hubbe): Insert the correct color space. http://crbug.com/647725 - Picture picture(picture_buffer_id, bitstream_id, gfx::Rect(size_), - gfx::ColorSpace(), - want_promotion_hint ? true : allow_overlay); - picture.set_size_changed(size_changed); - if (want_promotion_hint) { - picture.set_wants_promotion_hint(true); - // This will prevent it from actually being promoted if it shouldn't be. - picture.set_texture_owner(!allow_overlay); - } - - // Notify picture ready before calling UseCodecBufferForPictureBuffer() since - // that process may be slow and shouldn't delay delivery of the frame to the - // renderer. The picture is only used on the same thread as this method is - // called, so it is safe to do this. - NotifyPictureReady(picture); - - // Connect the PictureBuffer to the decoded frame. - picture_buffer_manager_.UseCodecBufferForPictureBuffer(codec_buffer_index, - picture_buffer); -} - -void AndroidVideoDecodeAccelerator::Decode(BitstreamBuffer bitstream_buffer) { - DCHECK(thread_checker_.CalledOnValidThread()); - - // If we deferred getting a surface, then start getting one now. - if (defer_surface_creation_) { - // We should still be in BEFORE_OVERLAY_INIT, since we've deferred doing it - // until now. - DCHECK_EQ(state_, BEFORE_OVERLAY_INIT); - defer_surface_creation_ = false; - StartSurfaceChooser(); - if (state_ == ERROR) { - DLOG(ERROR) << "Failed deferred surface and MediaCodec initialization."; - return; - } - } - - // If we previously deferred a codec restart, take care of it now. This can - // happen on older devices where configuration changes require a codec reset. - if (codec_needs_reset_) { - DCHECK(!drain_type_); - ResetCodecState(); - } - - if (bitstream_buffer.id() >= 0 && bitstream_buffer.size() > 0) { - DecodeBuffer(std::move(bitstream_buffer)); - return; - } - - if (bitstream_buffer.id() < 0) { - NOTIFY_ERROR(INVALID_ARGUMENT, - "Invalid bistream_buffer, id: " << bitstream_buffer.id()); - } else { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce( - &AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer, - weak_this_factory_.GetWeakPtr(), bitstream_buffer.id())); - } -} - -void AndroidVideoDecodeAccelerator::DecodeBuffer( - BitstreamBuffer bitstream_buffer) { - pending_bitstream_records_.push(BitstreamRecord(std::move(bitstream_buffer))); - TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", - pending_bitstream_records_.size()); - - DoIOTask(true); -} - -void AndroidVideoDecodeAccelerator::RequestPictureBuffers() { - if (client_) { - // Allocate a picture buffer that is the actual frame size. Note that it - // will be an external texture anyway, so it doesn't allocate an image of - // that size. It's important to get the coded size right, so that - // VideoLayerImpl doesn't try to scale the texture when building the quad - // for it. - client_->ProvidePictureBuffers(kNumPictureBuffers, PIXEL_FORMAT_UNKNOWN, 1, - size_, - AVDAPictureBufferManager::kTextureTarget); - } -} - -void AndroidVideoDecodeAccelerator::AssignPictureBuffers( - const std::vector<PictureBuffer>& buffers) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(output_picture_buffers_.empty()); - DCHECK(free_picture_ids_.empty()); - - if (buffers.size() < kNumPictureBuffers) { - NOTIFY_ERROR(INVALID_ARGUMENT, "Not enough picture buffers assigned."); - return; - } - - const bool have_context = make_context_current_cb_.Run(); - LOG_IF(WARNING, !have_context) - << "Failed to make GL context current for Assign, continuing."; - - for (size_t i = 0; i < buffers.size(); ++i) { - DCHECK(buffers[i].size() == size_); - int32_t id = buffers[i].id(); - output_picture_buffers_.insert(std::make_pair(id, buffers[i])); - free_picture_ids_.push(id); - - picture_buffer_manager_.AssignOnePictureBuffer(buffers[i], have_context); - } - TRACE_COUNTER1("media", "AVDA::FreePictureIds", free_picture_ids_.size()); - DoIOTask(true); -} - -void AndroidVideoDecodeAccelerator::ReusePictureBuffer( - int32_t picture_buffer_id) { - DCHECK(thread_checker_.CalledOnValidThread()); - - free_picture_ids_.push(picture_buffer_id); - TRACE_COUNTER1("media", "AVDA::FreePictureIds", free_picture_ids_.size()); - - auto it = output_picture_buffers_.find(picture_buffer_id); - if (it == output_picture_buffers_.end()) { - NOTIFY_ERROR(PLATFORM_FAILURE, - "Can't find PictureBuffer id " << picture_buffer_id); - return; - } - - picture_buffer_manager_.ReuseOnePictureBuffer(it->second); - DoIOTask(true); -} - -void AndroidVideoDecodeAccelerator::Flush() { - DVLOG(1) << __func__; - DCHECK(thread_checker_.CalledOnValidThread()); - StartCodecDrain(DRAIN_FOR_FLUSH); -} - -void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(!media_codec_); - DCHECK_NE(state_, WAITING_FOR_CODEC); - state_ = WAITING_FOR_CODEC; - - codec_allocator_->CreateMediaCodecAsync(weak_this_factory_.GetWeakPtr(), - codec_config_); -} - -void AndroidVideoDecodeAccelerator::ConfigureMediaCodecSynchronously() { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(!media_codec_); - DCHECK_NE(state_, WAITING_FOR_CODEC); - state_ = WAITING_FOR_CODEC; - - std::unique_ptr<MediaCodecBridge> media_codec = - codec_allocator_->CreateMediaCodecSync(codec_config_); - OnCodecConfigured(std::move(media_codec), codec_config_->surface_bundle); -} - -void AndroidVideoDecodeAccelerator::OnCodecConfigured( - std::unique_ptr<MediaCodecBridge> media_codec, - scoped_refptr<AVDASurfaceBundle> surface_bundle) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED); - // If we are supposed to notify that initialization is complete, then do so - // before returning. Otherwise, this is a reconfiguration. - - DCHECK(!media_codec_); - media_codec_ = std::move(media_codec); - - // If |state_| changed to SURFACE_DESTROYED while we were configuring a codec, - // then the codec is already invalid so we return early and drop it. - if (state_ == SURFACE_DESTROYED) { - if (deferred_initialization_pending_) { - // Losing the output surface is not considered an error state, so notify - // success. The client will destroy |this| soon. - NotifyInitializationSucceeded(); - } - - // Post it to the right thread. - ReleaseCodecAndBundle(); - return; - } - - picture_buffer_manager_.CodecChanged(media_codec_.get()); - if (!media_codec_) { - NOTIFY_ERROR(PLATFORM_FAILURE, "Failed to create MediaCodec"); - return; - } - - if (deferred_initialization_pending_) - NotifyInitializationSucceeded(); - - state_ = NO_ERROR; - - ManageTimer(true); -} - -void AndroidVideoDecodeAccelerator::StartCodecDrain(DrainType drain_type) { - DVLOG(2) << __func__ << " drain_type:" << drain_type; - DCHECK(thread_checker_.CalledOnValidThread()); - - auto previous_drain_type = drain_type_; - drain_type_ = drain_type; - - // Only DRAIN_FOR_DESTROY is allowed while a drain is already in progress. - DCHECK(!previous_drain_type || drain_type == DRAIN_FOR_DESTROY) - << "StartCodecDrain(" << drain_type - << ") while already draining with type " << previous_drain_type.value(); - - // Skip the drain if: - // * There's no codec. - // * The codec is not currently decoding and we have no more inputs to submit. - // (Reset() and Destroy() should clear pending inputs before calling this). - // * The drain is for reset or destroy (where we can drop pending decodes) and - // the codec is not VP8. We still have to drain VP8 in this case because - // MediaCodec can hang in release() or flush() if we don't drain it. - // http://crbug.com/598963 - if (!media_codec_ || - (pending_bitstream_records_.empty() && - bitstream_buffers_in_decoder_.empty()) || - (drain_type != DRAIN_FOR_FLUSH && codec_config_->codec != kCodecVP8)) { - OnDrainCompleted(); - return; - } - - // Queue EOS if one is not already queued. - if (!previous_drain_type) - DecodeBuffer(BitstreamBuffer(-1, base::SharedMemoryHandle(), - false /* read_only */, 0)); -} - -bool AndroidVideoDecodeAccelerator::IsDrainingForResetOrDestroy() const { - return drain_type_ == DRAIN_FOR_RESET || drain_type_ == DRAIN_FOR_DESTROY; -} - -void AndroidVideoDecodeAccelerator::OnDrainCompleted() { - DVLOG(2) << __func__; - DCHECK(thread_checker_.CalledOnValidThread()); - - // Sometimes MediaCodec returns an EOS buffer even if we didn't queue one. - // Consider it an error. http://crbug.com/585959. - if (!drain_type_) { - NOTIFY_ERROR(PLATFORM_FAILURE, "Unexpected EOS"); - return; - } - - switch (*drain_type_) { - case DRAIN_FOR_FLUSH: - ResetCodecState(); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&AndroidVideoDecodeAccelerator::NotifyFlushDone, - weak_this_factory_.GetWeakPtr())); - break; - case DRAIN_FOR_RESET: - ResetCodecState(); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&AndroidVideoDecodeAccelerator::NotifyResetDone, - weak_this_factory_.GetWeakPtr())); - break; - case DRAIN_FOR_DESTROY: - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&AndroidVideoDecodeAccelerator::ActualDestroy, - weak_this_factory_.GetWeakPtr())); - break; - } - drain_type_.reset(); -} - -void AndroidVideoDecodeAccelerator::ResetCodecState() { - DCHECK(thread_checker_.CalledOnValidThread()); - - // If there is already a reset in flight, then that counts. This can really - // only happen if somebody calls Reset. - // If the surface is destroyed or we're in an error state there's nothing to - // do. Note that BEFORE_OVERLAY_INIT implies that we have no codec, but it's - // included for completeness. - if (state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED || - state_ == BEFORE_OVERLAY_INIT || state_ == ERROR || !media_codec_) { - return; - } - - bitstream_buffers_in_decoder_.clear(); - - if (pending_input_buf_index_ != -1) { - // The data for that index exists in the input buffer, but corresponding - // shm block been deleted. Check that it is safe to flush the codec, i.e. - // |pending_bitstream_records_| is empty. - // TODO(timav): keep shm block for that buffer and remove this restriction. - DCHECK(pending_bitstream_records_.empty()); - pending_input_buf_index_ = -1; - } - - // If we've just completed a flush don't reset the codec yet. Instead defer - // until the next decode call. This prevents us from unbacking frames that - // might be out for display at end of stream. - codec_needs_reset_ = - (drain_type_ == DRAIN_FOR_FLUSH) || (drain_type_ == DRAIN_FOR_RESET); - if (codec_needs_reset_) - return; - - // Flush the codec if possible, or create a new one if not. - if (!MediaCodecUtil::CodecNeedsFlushWorkaround(media_codec_.get())) { - DVLOG(3) << __func__ << " Flushing MediaCodec."; - media_codec_->Flush(); - // Since we just flushed all the output buffers, make sure that nothing is - // using them. - picture_buffer_manager_.CodecChanged(media_codec_.get()); - } else { - DVLOG(3) << __func__ << " Deleting the MediaCodec and creating a new one."; - GetManager()->StopTimer(this); - // Release the codec, retain the bundle, and allocate a new codec. It will - // not wait for the old one to finish up with the bundle, which is bad. It - // works (usually) because it ends up allocating the codec on the same - // thread as is used to release the old one, so it's serialized anyway. - ReleaseCodec(); - ConfigureMediaCodecAsynchronously(); - } -} - -void AndroidVideoDecodeAccelerator::Reset() { - DVLOG(1) << __func__; - DCHECK(thread_checker_.CalledOnValidThread()); - TRACE_EVENT0("media", "AVDA::Reset"); - - if (defer_surface_creation_) { - DCHECK(!media_codec_); - DCHECK(pending_bitstream_records_.empty()); - DCHECK_EQ(state_, BEFORE_OVERLAY_INIT); - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&AndroidVideoDecodeAccelerator::NotifyResetDone, - weak_this_factory_.GetWeakPtr())); - return; - } - - while (!pending_bitstream_records_.empty()) { - int32_t bitstream_buffer_id = - pending_bitstream_records_.front().buffer.id(); - pending_bitstream_records_.pop(); - - if (bitstream_buffer_id != -1) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce( - &AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer, - weak_this_factory_.GetWeakPtr(), bitstream_buffer_id)); - } - } - TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", 0); - bitstreams_notified_in_advance_.clear(); - - picture_buffer_manager_.ReleaseCodecBuffers(output_picture_buffers_); - StartCodecDrain(DRAIN_FOR_RESET); -} - -void AndroidVideoDecodeAccelerator::SetOverlayInfo( - const OverlayInfo& overlay_info) { - DVLOG(1) << __func__; - DCHECK(thread_checker_.CalledOnValidThread()); - - if (state_ == ERROR) - return; - - // Update |config_| to contain the most recent info. Also save a copy, so - // that we can check for duplicate info later. - OverlayInfo previous_info = config_.overlay_info; - config_.overlay_info = overlay_info; - - // It's possible that we'll receive SetSurface before initializing the surface - // chooser. For example, if we defer surface creation, then we'll signal - // success to WMPI before initializing it. WMPI is then free to change - // |surface_id|. In this case, take no additional action, since |config_| is - // up to date. We'll use it later. - if (state_ == BEFORE_OVERLAY_INIT) - return; - - // Notify the chooser about the fullscreen state. - surface_chooser_helper_.SetIsFullscreen(overlay_info.is_fullscreen); - - // Note that these might be kNoSurfaceID / empty. In that case, we will - // revoke the factory. - OverlayInfo::RoutingToken routing_token = overlay_info.routing_token; - - // We don't want to change the factory unless this info has actually changed. - // We'll get the same info many times if some other part of the config is now - // different, such as fullscreen state. - base::Optional<AndroidOverlayFactoryCB> new_factory; - if (routing_token != previous_info.routing_token) { - if (routing_token && overlay_factory_cb_) - new_factory = base::BindRepeating(overlay_factory_cb_, *routing_token); - } - - surface_chooser_helper_.UpdateChooserState(new_factory); -} - -void AndroidVideoDecodeAccelerator::Destroy() { - DVLOG(1) << __func__; - DCHECK(thread_checker_.CalledOnValidThread()); - - picture_buffer_manager_.Destroy(output_picture_buffers_); - client_ = nullptr; - - // We don't want to queue more inputs while draining. - base::queue<BitstreamRecord>().swap(pending_bitstream_records_); - StartCodecDrain(DRAIN_FOR_DESTROY); -} - -void AndroidVideoDecodeAccelerator::ActualDestroy() { - DVLOG(1) << __func__; - DCHECK(thread_checker_.CalledOnValidThread()); - - // Note that async codec construction might still be in progress. In that - // case, the codec will be deleted when it completes once we invalidate all - // our weak refs. - weak_this_factory_.InvalidateWeakPtrs(); - GetManager()->StopTimer(this); - // We only release the codec here, in case codec allocation is in progress. - // We don't want to modify |codec_config_|. Note that the ref will sill be - // dropped when it completes, or when we delete |this|. - ReleaseCodec(); - - delete this; -} - -bool AndroidVideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread( - const base::WeakPtr<Client>& decode_client, - const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner) { - return false; -} - -const gfx::Size& AndroidVideoDecodeAccelerator::GetSize() const { - return size_; -} - -gpu::gles2::ContextGroup* AndroidVideoDecodeAccelerator::GetContextGroup() - const { - return get_context_group_cb_.Run(); -} - -std::unique_ptr<gpu::gles2::AbstractTexture> -AndroidVideoDecodeAccelerator::CreateAbstractTexture(GLenum target, - GLenum internal_format, - GLsizei width, - GLsizei height, - GLsizei depth, - int border, - GLenum format, - GLenum type) { - return create_abstract_texture_cb_.Run(target, internal_format, width, height, - depth, border, format, type); -} - -void AndroidVideoDecodeAccelerator::OnStopUsingOverlayImmediately( - AndroidOverlay* overlay) { - DVLOG(1) << __func__; - TRACE_EVENT0("media", "AVDA::OnStopUsingOverlayImmediately"); - DCHECK(thread_checker_.CalledOnValidThread()); - - // We cannot get here if we're before surface allocation, since we transition - // to WAITING_FOR_CODEC (or NO_ERROR, if sync) when we get the surface without - // posting. If we do ever lose the surface before starting codec allocation, - // then we could just update the config to use a TextureOwner and return - // without changing state. - DCHECK_NE(state_, BEFORE_OVERLAY_INIT); - - // If we're transitioning to |overlay|, then just stop here. We're not also - // using the overlay if we're transitioning to it. - if (!!incoming_overlay_ && incoming_overlay_->get() == overlay) { - incoming_overlay_.reset(); - return; - } - - // If we have no codec, or if our current config doesn't refer to |overlay|, - // then do nothing. |overlay| might be for some overlay that's waiting for - // codec destruction on some other thread. - if (!codec_config_->surface_bundle || - codec_config_->surface_bundle->overlay.get() != overlay) { - return; - } - - // If we have a codec, or if codec allocation is in flight, then it's using an - // overlay that was destroyed. - if (state_ == WAITING_FOR_CODEC) { - // What we should do here is to set |incoming_overlay_| to nullptr, to start - // a transistion to TextureOwner. OnCodecConfigured could notice that - // there's an incoming overlay, and then immediately transition the codec / - // drop and re-allocate the codec using it. However, for CVV, that won't - // work, since CVV-based overlays block the main thread waiting for the - // overlay to be dropped, so OnCodecConfigured won't run. For DS, it's the - // right thing. - // So, for now, we just fail, and let OnCodecConfigured drop the codec. - // Note that this case really can only happen on pre-M anyway, unless it's - // during initial construction. This will result in the overlay being - // destroyed after timeout, since OnCodecConfigured can't run until the - // synchronous CVV destruction quits. - state_ = SURFACE_DESTROYED; - return; - } - - // If the API is available avoid having to restart the decoder in order to - // leave fullscreen. If we don't clear the surface immediately during this - // callback, the MediaCodec will throw an error as the surface is destroyed. - if (device_info_->IsSetOutputSurfaceSupported()) { - // Since we can't wait for a transition, we must invalidate all outstanding - // picture buffers to avoid putting the GL system in a broken state. - picture_buffer_manager_.ReleaseCodecBuffers(output_picture_buffers_); - - // If we aren't transitioning to some other surface, then transition to a - // TextureOwner. Remember that, if |incoming_overlay_| is an overlay, - // then it's already ready and can be transitioned to immediately. We were - // just waiting for codec buffers to come back, but we just dropped them. - // Note that we want |incoming_overlay_| to has_value(), but that value - // should be a nullptr to indicate that we should switch to TextureOwner. - if (!incoming_overlay_) - incoming_overlay_ = std::unique_ptr<AndroidOverlay>(); - - UpdateSurface(); - // Switching to a TextureOwner should never need to wait. If it does, - // then the codec might still be using the destroyed surface, which is bad. - return; - } - - // If we're currently asynchronously configuring a codec, it will be destroyed - // when configuration completes and it notices that |state_| has changed to - // SURFACE_DESTROYED. It's safe to modify |codec_config_| here, since we - // checked above for WAITING_FOR_CODEC. - state_ = SURFACE_DESTROYED; - ReleaseCodecAndBundle(); - - // If we're draining, signal completion now because the drain can no longer - // proceed. - if (drain_type_) - OnDrainCompleted(); -} - -void AndroidVideoDecodeAccelerator::InitializeCdm() { - DVLOG(2) << __func__ << ": " << config_.cdm_id; - -#if !BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) - NOTREACHED(); -#else - // Store the CDM to hold a reference to it. - cdm_for_reference_holding_only_ = - CdmManager::GetInstance()->GetCdm(config_.cdm_id); - - // We can DCHECK here and below because we checked HasValidCdm() before - // calling InitializeCdm(), and the status shouldn't have changed since then. - DCHECK(cdm_for_reference_holding_only_) << "CDM not available"; - - media_crypto_context_ = - cdm_for_reference_holding_only_->GetCdmContext()->GetMediaCryptoContext(); - DCHECK(media_crypto_context_) << "MediaCryptoContext not available."; - - // Deferred initialization will continue in OnMediaCryptoReady(). The callback - // registered will be posted back to this thread via BindToCurrentLoop. - media_crypto_context_->SetMediaCryptoReadyCB(BindToCurrentLoop( - base::Bind(&AndroidVideoDecodeAccelerator::OnMediaCryptoReady, - weak_this_factory_.GetWeakPtr()))); -#endif // !BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) -} - -void AndroidVideoDecodeAccelerator::OnMediaCryptoReady( - JavaObjectPtr media_crypto, - bool requires_secure_video_codec) { - DVLOG(1) << __func__; - DCHECK(media_crypto); - - if (media_crypto->is_null()) { - media_crypto_context_->SetMediaCryptoReadyCB( - MediaCryptoContext::MediaCryptoReadyCB()); - media_crypto_context_ = nullptr; - cdm_for_reference_holding_only_ = nullptr; - - if (config_.is_encrypted()) { - LOG(ERROR) - << "MediaCrypto is not available, can't play encrypted stream."; - NOTIFY_ERROR(PLATFORM_FAILURE, "MediaCrypto is not available"); - return; - } - - // MediaCrypto is not available, but the stream is clear. So we can still - // play the current stream. But if we switch to an encrypted stream playback - // will fail. - StartSurfaceChooser(); - return; - } - - // We assume this is a part of the initialization process, thus MediaCodec - // is not created yet. - DCHECK(!media_codec_); - DCHECK(deferred_initialization_pending_); - - // Since |this| holds a reference to the |cdm_|, by the time the CDM is - // destructed, UnregisterPlayer() must have been called and |this| has been - // destructed as well. So the |cdm_unset_cb| will never have a chance to be - // called. - // TODO(xhwang): Remove |cdm_unset_cb| after it's not used on all platforms. - cdm_registration_id_ = media_crypto_context_->RegisterPlayer( - BindToCurrentLoop(base::Bind(&AndroidVideoDecodeAccelerator::OnKeyAdded, - weak_this_factory_.GetWeakPtr())), - base::DoNothing()); - - codec_config_->media_crypto = std::move(media_crypto); - codec_config_->requires_secure_codec = requires_secure_video_codec; - - // Request a secure surface in all cases. For L3, it's okay if we fall back - // to TextureOwner rather than fail composition. For L1, it's required. - // It's also required if the command line says so. - surface_chooser_helper_.SetSecureSurfaceMode( - requires_secure_video_codec - ? SurfaceChooserHelper::SecureSurfaceMode::kRequired - : SurfaceChooserHelper::SecureSurfaceMode::kRequested); - - // After receiving |media_crypto_| we can start with surface creation. - StartSurfaceChooser(); -} - -void AndroidVideoDecodeAccelerator::OnKeyAdded() { - DVLOG(1) << __func__; - - // This can also be called before initial surface allocation has completed, - // so we might not have a surface / codec yet. In that case, we'll never - // transition to WAITING_FOR_KEY, which is fine. - if (state_ == WAITING_FOR_KEY) - state_ = NO_ERROR; - - DoIOTask(true); -} - -void AndroidVideoDecodeAccelerator::NotifyInitializationSucceeded() { - DCHECK(deferred_initialization_pending_); - - if (client_) - client_->NotifyInitializationComplete(true); - deferred_initialization_pending_ = false; -} - -void AndroidVideoDecodeAccelerator::NotifyPictureReady(const Picture& picture) { - if (client_) - client_->PictureReady(picture); -} - -void AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer( - int input_buffer_id) { - if (client_) - client_->NotifyEndOfBitstreamBuffer(input_buffer_id); -} - -void AndroidVideoDecodeAccelerator::NotifyFlushDone() { - if (client_) - client_->NotifyFlushDone(); -} - -void AndroidVideoDecodeAccelerator::NotifyResetDone() { - if (client_) - client_->NotifyResetDone(); -} - -void AndroidVideoDecodeAccelerator::NotifyError(Error error) { - state_ = ERROR; - - // If we're in the middle of Initialize, then stop. It will notice |state_|. - if (during_initialize_) - return; - - // If deferred init is pending, then notify the client that it failed. - if (deferred_initialization_pending_) { - if (client_) - client_->NotifyInitializationComplete(false); - deferred_initialization_pending_ = false; - return; - } - - // We're after all init. Just signal an error. - if (client_) - client_->NotifyError(error); -} - -PromotionHintAggregator::NotifyPromotionHintCB -AndroidVideoDecodeAccelerator::GetPromotionHintCB() { - return base::Bind(&AndroidVideoDecodeAccelerator::NotifyPromotionHint, - weak_this_factory_.GetWeakPtr()); -} - -void AndroidVideoDecodeAccelerator::NotifyPromotionHint( - PromotionHintAggregator::Hint hint) { - bool is_using_overlay = - codec_config_->surface_bundle && codec_config_->surface_bundle->overlay; - surface_chooser_helper_.NotifyPromotionHintAndUpdateChooser(hint, - is_using_overlay); -} - -void AndroidVideoDecodeAccelerator::ManageTimer(bool did_work) { - bool should_be_running = true; - - base::TimeTicks now = base::TimeTicks::Now(); - if (!did_work && !most_recent_work_.is_null()) { - // Make sure that we have done work recently enough, else stop the timer. - if (now - most_recent_work_ > IdleTimerTimeOut) { - most_recent_work_ = base::TimeTicks(); - should_be_running = false; - } - } else { - most_recent_work_ = now; - } - - if (should_be_running) - GetManager()->StartTimer(this); - else - GetManager()->StopTimer(this); -} - -// static -VideoDecodeAccelerator::Capabilities -AndroidVideoDecodeAccelerator::GetCapabilities( - const gpu::GpuPreferences& gpu_preferences) { - Capabilities capabilities; - SupportedProfiles& profiles = capabilities.supported_profiles; - - if (MediaCodecUtil::IsVp8DecoderAvailable()) { - SupportedProfile profile; - profile.profile = VP8PROFILE_ANY; - // Since there is little to no power benefit below 360p, don't advertise - // support for it. Let libvpx decode it, and save a MediaCodec instance. - // Note that we allow it anyway for encrypted content, since we push a - // separate profile for that. - profile.min_resolution.SetSize(480, 360); - profile.max_resolution.SetSize(3840, 2160); - // If we know MediaCodec will just create a software codec, prefer our - // internal software decoder instead. It's more up to date and secured - // within the renderer sandbox. However if the content is encrypted, we - // must use MediaCodec anyways since MediaDrm offers no way to decrypt - // the buffers and let us use our internal software decoders. - profile.encrypted_only = MediaCodecUtil::IsKnownUnaccelerated( - kCodecVP8, MediaCodecDirection::DECODER); - profiles.push_back(profile); - - // Always allow encrypted content, even at low resolutions. - profile.min_resolution.SetSize(0, 0); - profile.encrypted_only = true; - profiles.push_back(profile); - } - - if (MediaCodecUtil::IsVp9DecoderAvailable()) { - const VideoCodecProfile profile_types[] = { - VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE1, VP9PROFILE_PROFILE2, - VP9PROFILE_PROFILE3, VIDEO_CODEC_PROFILE_UNKNOWN}; - const bool is_known_unaccelerated = MediaCodecUtil::IsKnownUnaccelerated( - kCodecVP9, MediaCodecDirection::DECODER); - for (int i = 0; profile_types[i] != VIDEO_CODEC_PROFILE_UNKNOWN; i++) { - SupportedProfile profile; - // Limit to 360p, like we do for vp8. See above. - profile.min_resolution.SetSize(480, 360); - profile.max_resolution.SetSize(3840, 2160); - // If we know MediaCodec will just create a software codec, prefer our - // internal software decoder instead. It's more up to date and secured - // within the renderer sandbox. However if the content is encrypted, we - // must use MediaCodec anyways since MediaDrm offers no way to decrypt - // the buffers and let us use our internal software decoders. - profile.encrypted_only = is_known_unaccelerated; - profile.profile = profile_types[i]; - profiles.push_back(profile); - - // Always allow encrypted content. - profile.min_resolution.SetSize(0, 0); - profile.encrypted_only = true; - profiles.push_back(profile); - } - } - -#if BUILDFLAG(USE_PROPRIETARY_CODECS) - for (const auto& supported_profile : kSupportedH264Profiles) { - SupportedProfile profile; - profile.profile = supported_profile; - profile.min_resolution.SetSize(0, 0); - // Advertise support for 4k and let the MediaCodec fail when decoding if it - // doesn't support the resolution. It's assumed that consumers won't have - // software fallback for H264 on Android anyway. - profile.max_resolution.SetSize(3840, 2160); - profiles.push_back(profile); - } - -#if BUILDFLAG(ENABLE_HEVC_DEMUXING) - for (const auto& supported_profile : kSupportedHevcProfiles) { - SupportedProfile profile; - profile.profile = supported_profile; - profile.min_resolution.SetSize(0, 0); - profile.max_resolution.SetSize(3840, 2160); - profiles.push_back(profile); - } -#endif -#endif - - capabilities.flags = Capabilities::SUPPORTS_DEFERRED_INITIALIZATION | - Capabilities::NEEDS_ALL_PICTURE_BUFFERS_TO_DECODE | - Capabilities::SUPPORTS_ENCRYPTED_STREAMS; - - // If we're using threaded texture mailboxes the COPY_REQUIRED flag must be - // set on the video frames (http://crbug.com/582170), and SurfaceView output - // is disabled (http://crbug.com/582170). - if (gpu_preferences.enable_threaded_texture_mailboxes) { - capabilities.flags |= Capabilities::REQUIRES_TEXTURE_COPY; - } else if (MediaCodecUtil::IsSurfaceViewOutputSupported()) { - capabilities.flags |= Capabilities::SUPPORTS_EXTERNAL_OUTPUT_SURFACE; - if (MediaCodecUtil::IsSetOutputSurfaceSupported()) - capabilities.flags |= Capabilities::SUPPORTS_SET_EXTERNAL_OUTPUT_SURFACE; - } - - return capabilities; -} - -bool AndroidVideoDecodeAccelerator::IsMediaCodecSoftwareDecodingForbidden() - const { - // Prevent MediaCodec from using its internal software decoders when we have - // more secure and up to date versions in the renderer process. - return !config_.is_encrypted() && - (codec_config_->codec == kCodecVP8 || - codec_config_->codec == kCodecVP9) && - !force_allow_software_decoding_for_testing_; -} - -bool AndroidVideoDecodeAccelerator::UpdateSurface() { - DCHECK(incoming_overlay_); - DCHECK_NE(state_, WAITING_FOR_CODEC); - - // Start surface creation. Note that if we're called via surfaceDestroyed, - // then this must complete synchronously or it will DCHECK. Otherwise, we - // might still be using the destroyed surface. We don't enforce this, but - // it's worth remembering that there are cases where it's required. - // Note that we don't re-use |surface_bundle|, since the codec is using it! - incoming_bundle_ = - new AVDASurfaceBundle(std::move(incoming_overlay_.value())); - incoming_overlay_.reset(); - InitializePictureBufferManager(); - if (state_ == ERROR) { - // This might be called from OnSurfaceDestroyed(), so we have to release the - // MediaCodec if we failed to switch the surface. We reset the surface ID - // to the previous one, since failures never result in the codec using the - // new surface. This is only guaranteed because of how OnCodecConfigured - // works. If it could fail after getting a codec, then this assumption - // wouldn't be necessarily true anymore. - // Also note that we might not have switched surfaces yet, which is also bad - // for OnSurfaceDestroyed, because of BEFORE_OVERLAY_INIT. Shouldn't - // happen with TextureOwner, and OnSurfaceDestroyed checks for it. In - // either case, we definitely should not still have an incoming bundle; it - // should have been dropped. - DCHECK(!incoming_bundle_); - ReleaseCodecAndBundle(); - } - - return state_ != ERROR; -} - -void AndroidVideoDecodeAccelerator::ReleaseCodec() { - if (!media_codec_) - return; - - picture_buffer_manager_.CodecChanged(nullptr); - codec_allocator_->ReleaseMediaCodec(std::move(media_codec_), - codec_config_->surface_bundle); -} - -void AndroidVideoDecodeAccelerator::ReleaseCodecAndBundle() { - ReleaseCodec(); - codec_config_->surface_bundle = nullptr; -} - -void AndroidVideoDecodeAccelerator::CacheFrameInformation() { - bool is_using_overlay = - codec_config_->surface_bundle && codec_config_->surface_bundle->overlay; - - cached_frame_information_ = - surface_chooser_helper_.ComputeFrameInformation(is_using_overlay); -} - -} // namespace media
diff --git a/media/gpu/android/android_video_decode_accelerator.h b/media/gpu/android/android_video_decode_accelerator.h deleted file mode 100644 index d338828..0000000 --- a/media/gpu/android/android_video_decode_accelerator.h +++ /dev/null
@@ -1,429 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_GPU_ANDROID_ANDROID_VIDEO_DECODE_ACCELERATOR_H_ -#define MEDIA_GPU_ANDROID_ANDROID_VIDEO_DECODE_ACCELERATOR_H_ - -#include <stdint.h> - -#include <list> -#include <map> -#include <vector> - -#include "base/compiler_specific.h" -#include "base/containers/queue.h" -#include "base/optional.h" -#include "base/threading/thread_checker.h" -#include "base/timer/timer.h" -#include "gpu/command_buffer/service/gles2_cmd_decoder.h" -#include "gpu/config/gpu_preferences.h" -#include "media/base/android/media_codec_bridge_impl.h" -#include "media/base/android/media_crypto_context.h" -#include "media/base/android_overlay_mojo_factory.h" -#include "media/base/content_decryption_module.h" -#include "media/gpu/android/avda_picture_buffer_manager.h" -#include "media/gpu/android/avda_state_provider.h" -#include "media/gpu/android/codec_allocator.h" -#include "media/gpu/android/device_info.h" -#include "media/gpu/android/surface_chooser_helper.h" -#include "media/gpu/gpu_video_decode_accelerator_helpers.h" -#include "media/gpu/media_gpu_export.h" -#include "media/video/video_decode_accelerator.h" -#include "ui/gl/android/scoped_java_surface.h" -#include "ui/gl/android/surface_texture.h" - -namespace media { -class AndroidVideoSurfaceChooser; -class PromotionHintAggregator; - -// A VideoDecodeAccelerator implementation for Android. This class decodes the -// encoded input stream using Android's MediaCodec. It handles the work of -// transferring data to and from MediaCodec, and delegates attaching MediaCodec -// output buffers to PictureBuffers to AVDAPictureBufferManager. -class MEDIA_GPU_EXPORT AndroidVideoDecodeAccelerator - : public VideoDecodeAccelerator, - public AVDAStateProvider, - public CodecAllocatorClient { - public: - static VideoDecodeAccelerator::Capabilities GetCapabilities( - const gpu::GpuPreferences& gpu_preferences); - - AndroidVideoDecodeAccelerator( - CodecAllocator* codec_allocator, - std::unique_ptr<AndroidVideoSurfaceChooser> surface_chooser, - const MakeGLContextCurrentCallback& make_context_current_cb, - const GetContextGroupCallback& get_context_group_cb, - const AndroidOverlayMojoFactoryCB& overlay_factory_cb, - const CreateAbstractTextureCallback& create_abstract_texture_cb, - DeviceInfo* device_info); - - ~AndroidVideoDecodeAccelerator() override; - - // VideoDecodeAccelerator implementation: - bool Initialize(const Config& config, Client* client) override; - void Decode(BitstreamBuffer bitstream_buffer) override; - void AssignPictureBuffers(const std::vector<PictureBuffer>& buffers) override; - void ReusePictureBuffer(int32_t picture_buffer_id) override; - void Flush() override; - void Reset() override; - void SetOverlayInfo(const OverlayInfo& overlay_info) override; - void Destroy() override; - bool TryToSetupDecodeOnSeparateThread( - const base::WeakPtr<Client>& decode_client, - const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner) - override; - - // AVDAStateProvider implementation: - const gfx::Size& GetSize() const override; - gpu::gles2::ContextGroup* GetContextGroup() const override; - std::unique_ptr<gpu::gles2::AbstractTexture> CreateAbstractTexture( - GLenum target, - GLenum internal_format, - GLsizei width, - GLsizei height, - GLsizei depth, - int border, - GLenum format, - GLenum type) override; - // Notifies the client about the error and sets |state_| to |ERROR|. If we're - // in the middle of Initialize, we guarantee that Initialize will return - // failure. If deferred init is pending, then we'll fail deferred init. - // Otherwise, we'll signal errors normally. - void NotifyError(Error error) override; - PromotionHintAggregator::NotifyPromotionHintCB GetPromotionHintCB() override; - - // CodecAllocatorClient implementation: - void OnCodecConfigured( - std::unique_ptr<MediaCodecBridge> media_codec, - scoped_refptr<AVDASurfaceBundle> surface_bundle) override; - - private: - friend class AVDAManager; - - // TODO(timav): evaluate the need for more states in the AVDA state machine. - enum State { - NO_ERROR, - ERROR, - // We haven't initialized |surface_chooser_| yet, so we don't have a surface - // or a codec. After we initialize |surface_chooser_|, we'll transition to - // WAITING_FOR_CODEC, NO_ERROR, or ERROR. - BEFORE_OVERLAY_INIT, - // Set when we are asynchronously constructing the codec. Will transition - // to NO_ERROR or ERROR depending on success. - WAITING_FOR_CODEC, - // Set when we have a codec, but it doesn't yet have a key. - WAITING_FOR_KEY, - // The output surface was destroyed. We must not configure a new MediaCodec - // with the destroyed surface. - SURFACE_DESTROYED, - }; - - enum DrainType { - DRAIN_FOR_FLUSH, - DRAIN_FOR_RESET, - DRAIN_FOR_DESTROY, - }; - - // Called once before (possibly deferred) initialization succeeds, to set up - // |surface_chooser_| with our initial factory from VDA::Config. - void StartSurfaceChooser(); - - // Start a transition to an overlay, or, if |!overlay|, TextureOwner. The - // transition doesn't have to be immediate; we'll favor not dropping frames. - void OnSurfaceTransition(std::unique_ptr<AndroidOverlay> overlay); - - // Called by AndroidOverlay when a surface is lost. We will discard pending - // frames, as needed, to switch away from |overlay| if we're using it. Before - // we return, we will have either dropped |overlay| if we own it, or posted - // it for async release with the codec that's using it. We also handle the - // case where we're not using |overlay| at all, since that can happen too - // while async codec release is pending. - void OnStopUsingOverlayImmediately(AndroidOverlay* overlay); - - // Initializes the picture buffer manager to use the current surface, once - // it is available. This is not normally called directly, but rather via - // StartSurfaceCreation. If we have a media codec already, then this will - // attempt to setSurface the new surface. Otherwise, it will start codec - // config using the new surface. In that case, there might not be a codec - // ready even if this succeeds, but async config will be started. If - // setSurface fails, this will not replace the codec. On failure, this will - // transition |state_| to ERROR. - // Note that this assumes that there is an |incoming_bundle_| that we'll use. - // On success, we'll replace the bundle in |codec_config_|. On failure, we'll - // delete the incoming bundle. - void InitializePictureBufferManager(); - - // A part of destruction process that is sometimes postponed after the drain. - void ActualDestroy(); - - // Configures |media_codec_| with the given codec parameters from the client. - // This configuration will (probably) not be complete before this call - // returns. Multiple calls before completion will be ignored. |state_| - // must be NO_ERROR or WAITING_FOR_CODEC. Note that, once you call this, - // you should be careful to avoid modifying members of |codec_config_| until - // |state_| is no longer WAITING_FOR_CODEC. - void ConfigureMediaCodecAsynchronously(); - - // Like ConfigureMediaCodecAsynchronously, but synchronous. Will NotifyError - // on failure. Since all configuration is done synchronously, there is no - // concern with modifying |codec_config_| after this returns. - void ConfigureMediaCodecSynchronously(); - - // Sends the decoded frame specified by |codec_buffer_index| to the client. - void SendDecodedFrameToClient(int32_t codec_buffer_index, - int32_t bitstream_id); - - // Does pending IO tasks if any. Once this is called, it polls |media_codec_| - // until it finishes pending tasks. For the polling, |kDecodePollDelay| is - // used. - void DoIOTask(bool start_timer); - - // Feeds buffers in |pending_bitstream_records_| to |media_codec_|. Returns - // true if one was queued. - bool QueueInput(); - - // Dequeues output from |media_codec_| and feeds the decoded frame to the - // client. Returns a hint about whether calling again might produce - // more output. - bool DequeueOutput(); - - // Requests picture buffers from the client. - void RequestPictureBuffers(); - - // Decode the content in the |bitstream_buffer|. Note that a - // |bitstream_buffer| of id as -1 indicates a flush command. - void DecodeBuffer(BitstreamBuffer bitstream_buffer); - - // Called during Initialize() for encrypted streams to set up the CDM. - void InitializeCdm(); - - // Called after the CDM obtains a MediaCrypto object. - void OnMediaCryptoReady(JavaObjectPtr media_crypto, - bool requires_secure_video_codec); - - // Called when a new key is added to the CDM. - void OnKeyAdded(); - - // Notifies the client that deferred initialization succeeded. If it fails, - // then call NotifyError instead. - void NotifyInitializationSucceeded(); - - // Notifies the client about the availability of a picture. - void NotifyPictureReady(const Picture& picture); - - // Notifies the client that the input buffer identifed by input_buffer_id has - // been processed. - void NotifyEndOfBitstreamBuffer(int input_buffer_id); - - // Notifies the client that the decoder was flushed. - void NotifyFlushDone(); - - // Notifies the client that the decoder was reset. - void NotifyResetDone(); - - // Start or stop our work-polling timer based on whether we did any work, and - // how long it has been since we've done work. Calling this with true will - // start the timer. Calling it with false may stop the timer. - void ManageTimer(bool did_work); - - // Start the MediaCodec drain process by adding end_of_stream() buffer to the - // encoded buffers queue. When we receive EOS from the output buffer the drain - // process completes and we perform the action depending on the |drain_type|. - void StartCodecDrain(DrainType drain_type); - - // Returns true if we are currently draining the codec and doing that as part - // of Reset() or Destroy() VP8 workaround. (http://crbug.com/598963). We won't - // display any frames and disable normal errors handling. - bool IsDrainingForResetOrDestroy() const; - - // A helper method that performs the operation required after the drain - // completion (usually when we receive EOS in the output). The operation - // itself depends on the |drain_type_|. - void OnDrainCompleted(); - - // Resets MediaCodec and buffers/containers used for storing output. These - // components need to be reset upon EOS to decode a later stream. Input state - // (e.g. queued BitstreamBuffers) is not reset, as input following an EOS - // is still valid and should be processed. - void ResetCodecState(); - - // Indicates if MediaCodec should not be used for software decoding since we - // have safer versions elsewhere. - bool IsMediaCodecSoftwareDecodingForbidden() const; - - // On platforms which support seamless surface changes, this will reinitialize - // the picture buffer manager with the new surface. This function reads and - // clears the surface id from |pending_surface_id_|. It will issue a decode - // error if the surface change fails. Returns false on failure. - bool UpdateSurface(); - - // Release |media_codec_| if it's not null, and notify - // |picture_buffer_manager_|. - void ReleaseCodec(); - - // ReleaseCodec(), and also drop our ref to it's surface bundle. This is - // the right thing to do unless you're planning to re-use the bundle with - // another codec. Normally, one doesn't. - void ReleaseCodecAndBundle(); - - // Send a |hint| to |promotion_hint_aggregator_|. - void NotifyPromotionHint(PromotionHintAggregator::Hint hint); - - // Used to DCHECK that we are called on the correct thread. - base::ThreadChecker thread_checker_; - - // To expose client callbacks from VideoDecodeAccelerator. - Client* client_; - - CodecAllocator* codec_allocator_; - - // Callback to set the correct gl context. - MakeGLContextCurrentCallback make_context_current_cb_; - - // Callback to get the ContextGroup*. - GetContextGroupCallback get_context_group_cb_; - - // The current state of this class. For now, this is used only for setting - // error state. - State state_; - - // The assigned picture buffers by picture buffer id. - AVDAPictureBufferManager::PictureBufferMap output_picture_buffers_; - - // This keeps the free picture buffer ids which can be used for sending - // decoded frames to the client. - base::queue<int32_t> free_picture_ids_; - - // The low-level decoder which Android SDK provides. - std::unique_ptr<MediaCodecBridge> media_codec_; - - // Set to true after requesting picture buffers to the client. - bool picturebuffers_requested_; - - // The resolution of the stream. - gfx::Size size_; - - // Handy structure to remember a BitstreamBuffer and also its shared memory, - // if any. The goal is to prevent leaving a BitstreamBuffer's shared memory - // handle open. - struct BitstreamRecord { - BitstreamRecord(BitstreamBuffer); - BitstreamRecord(BitstreamRecord&& other); - ~BitstreamRecord(); - - // The region in this buffer will not be valid, as it will have been passed - // to |memory|, below. - BitstreamBuffer buffer; - - // |memory| may be null if buffer has no data. - std::unique_ptr<UnalignedSharedMemory> memory; - }; - - // Encoded bitstream buffers to be passed to media codec, queued until an - // input buffer is available. - base::queue<BitstreamRecord> pending_bitstream_records_; - - // A map of presentation timestamp to bitstream buffer id for the bitstream - // buffers that have been submitted to the decoder but haven't yet produced an - // output frame with the same timestamp. Note: there will only be one entry - // for multiple bitstream buffers that have the same presentation timestamp. - std::map<base::TimeDelta, int32_t> bitstream_buffers_in_decoder_; - - // Keeps track of bitstream ids notified to the client with - // NotifyEndOfBitstreamBuffer() before getting output from the bitstream. - std::list<int32_t> bitstreams_notified_in_advance_; - - AVDAPictureBufferManager picture_buffer_manager_; - - // Time at which we last did useful work on io_timer_. - base::TimeTicks most_recent_work_; - - // The ongoing drain operation, if any. - base::Optional<DrainType> drain_type_; - - // Holds a ref-count to the CDM to avoid using the CDM after it's destroyed. - scoped_refptr<ContentDecryptionModule> cdm_for_reference_holding_only_; - - // Owned by CDM which is external to this decoder. - MediaCryptoContext* media_crypto_context_; - - // MediaDrmBridge requires registration/unregistration of the player, this - // registration id is used for this. - int cdm_registration_id_; - - // Configuration that we use for MediaCodec. - // Do not update any of its members while |state_| is WAITING_FOR_CODEC. - scoped_refptr<CodecConfig> codec_config_; - - // Index of the dequeued and filled buffer that we keep trying to enqueue. - // Such buffer appears in MEDIA_CODEC_NO_KEY processing. - int pending_input_buf_index_; - - // Monotonically increasing value that is used to prevent old, delayed errors - // from being sent after a reset. - int error_sequence_token_; - - // Are we currently processing a call to Initialize()? Please don't use this - // unless you're NotifyError. - bool during_initialize_; - - // True if and only if VDA initialization is deferred, and we have not yet - // called NotifyInitializationComplete. - bool deferred_initialization_pending_; - - // Indicates if ResetCodecState() should be called upon the next call to - // Decode(). Allows us to avoid trashing the last few frames of a playback - // when the EOS buffer is received. - bool codec_needs_reset_; - - // True if surface creation and |picture_buffer_manager_| initialization has - // been defered until the first Decode() call. - bool defer_surface_creation_; - - // Copy of the VDA::Config we were given. - Config config_; - - // SurfaceBundle that we're going to use for StartSurfaceCreation. This is - // separate than the bundle in |codec_config_|, since we can start surface - // creation while another codec is using the old surface. For example, if - // we're going to SetSurface, then the current codec will depend on the - // current bundle until then. - scoped_refptr<AVDASurfaceBundle> incoming_bundle_; - - // If we have been given an overlay to use, then this is it. If we've been - // told to move to TextureOwner, then this will be value() == nullptr. - base::Optional<std::unique_ptr<AndroidOverlay>> incoming_overlay_; - - SurfaceChooserHelper surface_chooser_helper_; - - DeviceInfo* device_info_; - - bool force_defer_surface_creation_for_testing_; - - bool force_allow_software_decoding_for_testing_; - - // Optional factory to produce mojo AndroidOverlay instances. - AndroidOverlayMojoFactoryCB overlay_factory_cb_; - - CreateAbstractTextureCallback create_abstract_texture_cb_; - - std::unique_ptr<PromotionHintAggregator> promotion_hint_aggregator_; - - // Update |cached_frame_information_|. - void CacheFrameInformation(); - - // Most recently cached frame information, so that we can dispatch it without - // recomputing it on every frame. It changes very rarely. - SurfaceChooserHelper::FrameInformation cached_frame_information_ = - SurfaceChooserHelper::FrameInformation::NON_OVERLAY_INSECURE; - - // WeakPtrFactory for posting tasks back to |this|. - base::WeakPtrFactory<AndroidVideoDecodeAccelerator> weak_this_factory_; - - friend class AndroidVideoDecodeAcceleratorTest; -}; - -} // namespace media - -#endif // MEDIA_GPU_ANDROID_ANDROID_VIDEO_DECODE_ACCELERATOR_H_
diff --git a/media/gpu/android/android_video_decode_accelerator_unittest.cc b/media/gpu/android/android_video_decode_accelerator_unittest.cc deleted file mode 100644 index 2f7c25e..0000000 --- a/media/gpu/android/android_video_decode_accelerator_unittest.cc +++ /dev/null
@@ -1,603 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/gpu/android/android_video_decode_accelerator.h" - -#include <stdint.h> - -#include <memory> - -#include "base/bind.h" -#include "base/logging.h" -#include "base/memory/weak_ptr.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/test/scoped_task_environment.h" -#include "base/threading/sequenced_task_runner_handle.h" -#include "gpu/command_buffer/client/client_test_helper.h" -#include "gpu/command_buffer/service/context_group.h" -#include "gpu/command_buffer/service/gpu_tracer.h" -#include "gpu/command_buffer/service/image_manager.h" -#include "gpu/command_buffer/service/mailbox_manager_impl.h" -#include "gpu/command_buffer/service/service_discardable_manager.h" -#include "gpu/command_buffer/service/shared_image_manager.h" -#include "media/base/android/media_codec_util.h" -#include "media/base/android/mock_android_overlay.h" -#include "media/base/android/mock_media_codec_bridge.h" -#include "media/gpu/android/android_video_decode_accelerator.h" -#include "media/gpu/android/android_video_surface_chooser.h" -#include "media/gpu/android/codec_allocator.h" -#include "media/gpu/android/fake_codec_allocator.h" -#include "media/gpu/android/mock_abstract_texture.h" -#include "media/gpu/android/mock_android_video_surface_chooser.h" -#include "media/gpu/android/mock_device_info.h" -#include "media/media_buildflags.h" -#include "media/video/picture.h" -#include "media/video/video_decode_accelerator.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gl/android/surface_texture.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_surface.h" -#include "ui/gl/init/gl_factory.h" - -using ::testing::NiceMock; -using ::testing::NotNull; -using ::testing::Return; -using ::testing::_; - -namespace media { -namespace { - -#define SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE() \ - do { \ - if (!MediaCodecUtil::IsMediaCodecAvailable()) \ - return; \ - } while (false) - -bool MakeContextCurrent() { - return true; -} - -gpu::gles2::ContextGroup* GetContextGroup( - scoped_refptr<gpu::gles2::ContextGroup> context_group) { - return context_group.get(); -} - -class MockVDAClient : public VideoDecodeAccelerator::Client { - public: - MockVDAClient() {} - - MOCK_METHOD1(NotifyInitializationComplete, void(bool)); - MOCK_METHOD5( - ProvidePictureBuffers, - void(uint32_t, VideoPixelFormat, uint32_t, const gfx::Size&, uint32_t)); - MOCK_METHOD1(DismissPictureBuffer, void(int32_t)); - MOCK_METHOD1(PictureReady, void(const Picture&)); - MOCK_METHOD1(NotifyEndOfBitstreamBuffer, void(int32_t)); - MOCK_METHOD0(NotifyFlushDone, void()); - MOCK_METHOD0(NotifyResetDone, void()); - MOCK_METHOD1(NotifyError, void(VideoDecodeAccelerator::Error)); - - private: - DISALLOW_COPY_AND_ASSIGN(MockVDAClient); -}; - -} // namespace - -class AndroidVideoDecodeAcceleratorTest - : public testing::TestWithParam<VideoCodecProfile> { - public: - // Default to baseline H264 because it's always supported. - AndroidVideoDecodeAcceleratorTest() : config_(GetParam()) {} - - void SetUp() override { - ASSERT_TRUE(gl::init::InitializeGLOneOff()); - surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size(16, 16)); - context_ = gl::init::CreateGLContext(nullptr, surface_.get(), - gl::GLContextAttribs()); - context_->MakeCurrent(surface_.get()); - - codec_allocator_ = std::make_unique<FakeCodecAllocator>( - base::SequencedTaskRunnerHandle::Get()); - device_info_ = std::make_unique<NiceMock<MockDeviceInfo>>(); - - chooser_that_is_usually_null_ = - std::make_unique<NiceMock<MockAndroidVideoSurfaceChooser>>(); - chooser_ = chooser_that_is_usually_null_.get(); - - feature_info_ = new gpu::gles2::FeatureInfo(); - context_group_ = new gpu::gles2::ContextGroup( - gpu_preferences_, false, &mailbox_manager_, nullptr, nullptr, nullptr, - feature_info_, false, &image_manager_, nullptr, nullptr, - gpu::GpuFeatureInfo(), &discardable_manager_, nullptr, - &shared_image_manager_); - - // By default, allow deferred init. - config_.is_deferred_initialization_allowed = true; - } - - ~AndroidVideoDecodeAcceleratorTest() override { - // ~AVDASurfaceBundle() might rely on GL being available, so we have to - // explicitly drop references to them before tearing down GL. - vda_ = nullptr; - codec_allocator_ = nullptr; - context_ = nullptr; - surface_ = nullptr; - feature_info_ = nullptr; - context_group_ = nullptr; - - gl::init::ShutdownGL(false); - } - - std::unique_ptr<AndroidOverlay> OverlayFactory(const base::UnguessableToken&, - AndroidOverlayConfig config) { - // This shouldn't be called by AVDA. Our mock surface chooser won't use it - // either, though it'd be nice to check to token. Note that this isn't the - // same as an emtpy factory callback; that means "no factory". This one - // looks like a working factory, as long as nobody calls it. - return nullptr; - } - - // Create and initialize AVDA with |config_|, and return the result. - bool InitializeAVDA(bool force_defer_surface_creation = false) { - // Because VDA has a custom deleter, we must assign it to |vda_| carefully. - AndroidVideoDecodeAccelerator* avda = new AndroidVideoDecodeAccelerator( - codec_allocator_.get(), std::move(chooser_that_is_usually_null_), - base::BindRepeating(&MakeContextCurrent), - base::BindRepeating(&GetContextGroup, context_group_), - base::BindRepeating(&AndroidVideoDecodeAcceleratorTest::OverlayFactory, - base::Unretained(this)), - base::BindRepeating( - &AndroidVideoDecodeAcceleratorTest::CreateAbstractTexture, - base::Unretained(this)), - device_info_.get()); - vda_.reset(avda); - avda->force_defer_surface_creation_for_testing_ = - force_defer_surface_creation; - avda->force_allow_software_decoding_for_testing_ = true; - - bool result = vda_->Initialize(config_, &client_); - base::RunLoop().RunUntilIdle(); - return result; - } - - std::unique_ptr<gpu::gles2::AbstractTexture> CreateAbstractTexture( - GLenum target, - GLenum internal_format, - GLsizei width, - GLsizei height, - GLsizei depth, - int border, - GLenum format, - GLenum type) { - return std::make_unique<MockAbstractTexture>(0); - } - - // Initialize |vda_|, providing a new surface for it. You may get the surface - // by asking |codec_allocator_|. - void InitializeAVDAWithOverlay() { - config_.overlay_info.routing_token = base::UnguessableToken::Create(); - ASSERT_TRUE(InitializeAVDA()); - base::RunLoop().RunUntilIdle(); - ASSERT_TRUE(chooser_->factory_); - - // Have the factory provide an overlay, and verify that codec creation is - // provided with that overlay. - std::unique_ptr<MockAndroidOverlay> overlay = - std::make_unique<MockAndroidOverlay>(); - overlay_callbacks_ = overlay->GetCallbacks(); - - // Set the expectations first, since ProvideOverlay might cause callbacks. - EXPECT_CALL(*codec_allocator_, - MockCreateMediaCodecAsync(overlay.get(), nullptr)); - chooser_->ProvideOverlay(std::move(overlay)); - - // Provide the codec so that we can check if it's freed properly. - EXPECT_CALL(client_, NotifyInitializationComplete(true)); - codec_allocator_->ProvideMockCodecAsync(); - base::RunLoop().RunUntilIdle(); - } - - void InitializeAVDAWithTextureOwner() { - ASSERT_TRUE(InitializeAVDA()); - base::RunLoop().RunUntilIdle(); - // We do not expect a factory, since we are using TextureOwner. - ASSERT_FALSE(chooser_->factory_); - - // Set the expectations first, since ProvideOverlay might cause callbacks. - EXPECT_CALL(*codec_allocator_, - MockCreateMediaCodecAsync(nullptr, NotNull())); - chooser_->ProvideTextureOwner(); - - // Provide the codec so that we can check if it's freed properly. - EXPECT_CALL(client_, NotifyInitializationComplete(true)); - codec_allocator_->ProvideMockCodecAsync(); - base::RunLoop().RunUntilIdle(); - } - - // Set whether HasUnrendereredPictureBuffers will return true or false. - // TODO(liberato): We can't actually do this yet. It turns out to be okay, - // because AVDA doesn't actually SetSurface before DequeueOutput. It could do - // so, though, if there aren't unrendered buffers. Should AVDA ever start - // switching surfaces immediately upon receiving them, rather than waiting for - // DequeueOutput, then we'll want to be able to indicate that it has - // unrendered pictures to prevent that behavior. - void SetHasUnrenderedPictureBuffers(bool flag) {} - - // Tell |avda_| to switch surfaces to its incoming surface. This is a method - // since we're a friend of AVDA, and the tests are subclasses. It's also - // somewhat hacky, but much less hacky than trying to run it via a timer. - void LetAVDAUpdateSurface() { - SetHasUnrenderedPictureBuffers(false); - avda()->DequeueOutput(); - } - - // So that SequencedTaskRunnerHandle::Get() works. - base::test::ScopedTaskEnvironment scoped_task_environment_; - - scoped_refptr<gl::GLSurface> surface_; - scoped_refptr<gl::GLContext> context_; - NiceMock<MockVDAClient> client_; - std::unique_ptr<FakeCodecAllocator> codec_allocator_; - - scoped_refptr<gpu::gles2::ContextGroup> context_group_; - scoped_refptr<gpu::gles2::FeatureInfo> feature_info_; - gpu::GpuPreferences gpu_preferences_; - gpu::gles2::MailboxManagerImpl mailbox_manager_; - gpu::gles2::ImageManager image_manager_; - gpu::ServiceDiscardableManager discardable_manager_; - gpu::SharedImageManager shared_image_manager_; - - // Only set until InitializeAVDA() is called. - std::unique_ptr<MockAndroidVideoSurfaceChooser> chooser_that_is_usually_null_; - MockAndroidVideoSurfaceChooser* chooser_; - VideoDecodeAccelerator::Config config_; - std::unique_ptr<MockDeviceInfo> device_info_; - - // Set by InitializeAVDAWithOverlay() - MockAndroidOverlay::Callbacks overlay_callbacks_; - - // This must be a unique pointer to a VDA, not an AVDA, to ensure the - // the default_delete specialization that calls Destroy() will be used. - std::unique_ptr<VideoDecodeAccelerator> vda_; - - AndroidVideoDecodeAccelerator* avda() { - return reinterpret_cast<AndroidVideoDecodeAccelerator*>(vda_.get()); - } -}; - -TEST_P(AndroidVideoDecodeAcceleratorTest, ConfigureUnsupportedCodec) { - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - - config_ = VideoDecodeAccelerator::Config(VIDEO_CODEC_PROFILE_UNKNOWN); - ASSERT_FALSE(InitializeAVDA()); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, - ConfigureSupportedCodecSynchronously) { - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - - config_.is_deferred_initialization_allowed = false; - - EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecSync(_, _)); - // AVDA must set client callbacks even in sync mode, so that the chooser is - // in a sane state. https://crbug.com/772899 . - EXPECT_CALL(*chooser_, MockSetClientCallbacks()); - ASSERT_TRUE(InitializeAVDA()); - testing::Mock::VerifyAndClearExpectations(chooser_); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, FailingToCreateACodecSyncIsAnError) { - // Failuew to create a codec during sync init should cause Initialize to fail. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - - config_.is_deferred_initialization_allowed = false; - codec_allocator_->allow_sync_creation = false; - - EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecSync(nullptr, NotNull())); - ASSERT_FALSE(InitializeAVDA()); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, FailingToCreateACodecAsyncIsAnError) { - // Verify that a null codec signals error for async init when it doesn't get a - // mediacodec instance. - // - // Also assert that there's only one call to CreateMediaCodecAsync. And since - // it replies with a null codec, AVDA will be in an error state when it shuts - // down. Since we know that it's constructed before we destroy the VDA, we - // verify that AVDA doens't create codecs during destruction. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - - // Note that if we somehow end up deferring surface creation, then this would - // no longer be expected to fail. It would signal success before asking for a - // surface or codec. - EXPECT_CALL(*codec_allocator_, MockCreateMediaCodecAsync(_, NotNull())); - EXPECT_CALL(client_, NotifyInitializationComplete(false)); - - ASSERT_TRUE(InitializeAVDA()); - chooser_->ProvideTextureOwner(); - codec_allocator_->ProvideNullCodecAsync(); - - // Make sure that codec allocation has happened before destroying the VDA. - testing::Mock::VerifyAndClearExpectations(codec_allocator_.get()); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, - LowEndDevicesSucceedInitWithoutASurface) { - // If AVDA decides that we should defer surface creation, then it should - // signal success before we provide a surface. It should still ask for a - // surface, though. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - - EXPECT_CALL(*chooser_, MockUpdateState()).Times(0); - EXPECT_CALL(client_, NotifyInitializationComplete(true)); - - // It would be nicer if we didn't just force this on, since we might do so - // in a state that AVDA isn't supposed to handle (e.g., if we give it a - // surface, then it would never decide to defer surface creation). - bool force_defer_surface_creation = true; - InitializeAVDA(force_defer_surface_creation); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, AsyncInitWithTextureOwnerAndDelete) { - // When configuring with a TextureOwner and deferred init, we should be - // asked for a codec, and be notified of init success if we provide one. When - // AVDA is destroyed, it should release the codec and texture owner. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - - InitializeAVDAWithTextureOwner(); - - // Delete the VDA, and make sure that it tries to free the codec and the right - // texture owner. - EXPECT_CALL( - *codec_allocator_, - MockReleaseMediaCodec(codec_allocator_->most_recent_codec, - codec_allocator_->most_recent_overlay, - codec_allocator_->most_recent_texture_owner)); - codec_allocator_->most_recent_codec_destruction_observer->ExpectDestruction(); - vda_ = nullptr; - base::RunLoop().RunUntilIdle(); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, AsyncInitWithSurfaceAndDelete) { - // When |config_| specifies a surface, we should be given a factory during - // startup for it. When |chooser_| provides an overlay, the codec should be - // allocated using it. Shutdown should provide the overlay when releasing the - // media codec. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - - InitializeAVDAWithOverlay(); - - // Delete the VDA, and make sure that it tries to free the codec and the - // overlay that it provided to us. - EXPECT_CALL( - *codec_allocator_, - MockReleaseMediaCodec(codec_allocator_->most_recent_codec, - codec_allocator_->most_recent_overlay, - codec_allocator_->most_recent_texture_owner)); - codec_allocator_->most_recent_codec_destruction_observer->ExpectDestruction(); - vda_ = nullptr; - base::RunLoop().RunUntilIdle(); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, - SwitchesToTextureOwnerWhenSurfaceDestroyed) { - // Provide a surface, and a codec, then destroy the surface. AVDA should use - // SetSurface to switch to TextureOwner. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - - InitializeAVDAWithOverlay(); - - // It would be nice if we knew that this was a texture owner. As it is, we - // just destroy the VDA and expect that we're provided with one. Hopefully, - // AVDA is actually calling SetSurface properly. - EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)) - .WillOnce(Return(true)); - codec_allocator_->most_recent_codec_destruction_observer - ->VerifyAndClearExpectations(); - overlay_callbacks_.SurfaceDestroyed.Run(); - base::RunLoop().RunUntilIdle(); - - EXPECT_CALL(*codec_allocator_, - MockReleaseMediaCodec(codec_allocator_->most_recent_codec, - nullptr, NotNull())); - vda_ = nullptr; - base::RunLoop().RunUntilIdle(); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, SwitchesToTextureOwnerEventually) { - // Provide a surface, and a codec, then request that AVDA switches to a - // texture owner. Verify that it does. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - - InitializeAVDAWithOverlay(); - - EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)) - .WillOnce(Return(true)); - - // Note that it's okay if |avda_| switches before ProvideTextureOwner - // returns, since it has no queued output anyway. - chooser_->ProvideTextureOwner(); - LetAVDAUpdateSurface(); - - // Verify that we're now using some texture owner. - EXPECT_CALL(*codec_allocator_, - MockReleaseMediaCodec(codec_allocator_->most_recent_codec, - nullptr, NotNull())); - codec_allocator_->most_recent_codec_destruction_observer->ExpectDestruction(); - vda_ = nullptr; - base::RunLoop().RunUntilIdle(); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, - SetSurfaceFailureDoesntSwitchSurfaces) { - // Initialize AVDA with a surface, then request that AVDA switches to a - // texture owner. When it tries to UpdateSurface, pretend to fail. AVDA - // should notify error, and also release the original surface. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - - InitializeAVDAWithOverlay(); - - EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)) - .WillOnce(Return(false)); - EXPECT_CALL(client_, - NotifyError(AndroidVideoDecodeAccelerator::PLATFORM_FAILURE)) - .Times(1); - codec_allocator_->most_recent_codec_destruction_observer - ->VerifyAndClearExpectations(); - chooser_->ProvideTextureOwner(); - LetAVDAUpdateSurface(); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, - SwitchToSurfaceAndBackBeforeSetSurface) { - // Ask AVDA to switch from ST to overlay, then back to ST before it has a - // chance to do the first switch. It should simply drop the overlay. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - - InitializeAVDAWithTextureOwner(); - - // Don't let AVDA switch immediately, else it could choose to SetSurface when - // it first gets the overlay. - SetHasUnrenderedPictureBuffers(true); - EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)).Times(0); - std::unique_ptr<MockAndroidOverlay> overlay = - std::make_unique<MockAndroidOverlay>(); - // Make sure that the overlay is not destroyed too soon. - std::unique_ptr<DestructionObserver> observer = - overlay->CreateDestructionObserver(); - observer->DoNotAllowDestruction(); - - chooser_->ProvideOverlay(std::move(overlay)); - - // Now it is expected to drop the overlay. - observer->ExpectDestruction(); - - // While the incoming surface is pending, switch back to TextureOwner. - chooser_->ProvideTextureOwner(); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, - ChangingOutputSurfaceVoluntarilyWithoutSetSurfaceIsIgnored) { - // If we ask AVDA to change to TextureOwner should be ignored on platforms - // that don't support SetSurface (pre-M or blacklisted). It should also - // ignore TextureOwner => overlay, but we don't check that. - // - // Also note that there are other probably reasonable things to do (like - // signal an error), but we want to be sure that it doesn't try to SetSurface. - // We also want to be sure that, if it doesn't signal an error, that it also - // doesn't get confused about which surface is in use. So, we assume that it - // doesn't signal an error, and we check that it releases the right surface - // with the codec. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - EXPECT_CALL(client_, NotifyError(_)).Times(0); - - ON_CALL(*device_info_, IsSetOutputSurfaceSupported()) - .WillByDefault(Return(false)); - InitializeAVDAWithOverlay(); - EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)).Times(0); - - // This should not switch to TextureOwner. - chooser_->ProvideTextureOwner(); - LetAVDAUpdateSurface(); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, - OnSurfaceDestroyedWithoutSetSurfaceFreesTheCodec) { - // If AVDA receives OnSurfaceDestroyed without support for SetSurface, then it - // should free the codec. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - ON_CALL(*device_info_, IsSetOutputSurfaceSupported()) - .WillByDefault(Return(false)); - InitializeAVDAWithOverlay(); - EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)).Times(0); - - // This should free the codec. - EXPECT_CALL( - *codec_allocator_, - MockReleaseMediaCodec(codec_allocator_->most_recent_codec, - codec_allocator_->most_recent_overlay, nullptr)); - codec_allocator_->most_recent_codec_destruction_observer->ExpectDestruction(); - overlay_callbacks_.SurfaceDestroyed.Run(); - base::RunLoop().RunUntilIdle(); - - // Verify that the codec has been released, since |vda_| will be destroyed - // soon. The expectations must be met before that. - testing::Mock::VerifyAndClearExpectations(&codec_allocator_); - codec_allocator_->most_recent_codec_destruction_observer - ->VerifyAndClearExpectations(); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, - MultipleTextureOwnerCallbacksAreIgnored) { - // Ask AVDA to switch to ST when it's already using ST, nothing should happen. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - - InitializeAVDAWithTextureOwner(); - - // This should do nothing. - EXPECT_CALL(*codec_allocator_->most_recent_codec, SetSurface(_)).Times(0); - chooser_->ProvideTextureOwner(); - - base::RunLoop().RunUntilIdle(); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, - OverlayInfoWithDuplicateSurfaceIDDoesntChangeTheFactory) { - // Send OverlayInfo with duplicate info, and verify that it doesn't change - // the factory. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - InitializeAVDAWithOverlay(); - - EXPECT_CALL(*chooser_, MockUpdateState()).Times(1); - EXPECT_CALL(*chooser_, MockReplaceOverlayFactory(_)).Times(0); - OverlayInfo overlay_info = config_.overlay_info; - avda()->SetOverlayInfo(overlay_info); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, - OverlayInfoWithNewSurfaceIDDoesChangeTheFactory) { - // Send OverlayInfo with new surface info, and verify that it does change the - // overlay factory. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - InitializeAVDAWithOverlay(); - - EXPECT_CALL(*chooser_, MockUpdateState()).Times(1); - OverlayInfo overlay_info = config_.overlay_info; - overlay_info.routing_token = base::UnguessableToken::Create(); - avda()->SetOverlayInfo(overlay_info); -} - -TEST_P(AndroidVideoDecodeAcceleratorTest, FullscreenSignalIsSentToChooser) { - // Send OverlayInfo that has |is_fullscreen| set, and verify that the chooser - // is notified about it. - SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); - InitializeAVDAWithOverlay(); - OverlayInfo overlay_info = config_.overlay_info; - overlay_info.is_fullscreen = !config_.overlay_info.is_fullscreen; - avda()->SetOverlayInfo(overlay_info); - ASSERT_EQ(chooser_->current_state_.is_fullscreen, overlay_info.is_fullscreen); -} - -static std::vector<VideoCodecProfile> GetTestList() { - std::vector<VideoCodecProfile> test_profiles; - -#if BUILDFLAG(USE_PROPRIETARY_CODECS) - if (MediaCodecUtil::IsMediaCodecAvailable()) - test_profiles.push_back(H264PROFILE_BASELINE); -#endif - - if (MediaCodecUtil::IsVp8DecoderAvailable()) - test_profiles.push_back(VP8PROFILE_ANY); - if (MediaCodecUtil::IsVp9DecoderAvailable()) - test_profiles.push_back(VP9PROFILE_PROFILE0); - return test_profiles; -} - -INSTANTIATE_TEST_SUITE_P(AndroidVideoDecodeAcceleratorTest, - AndroidVideoDecodeAcceleratorTest, - testing::ValuesIn(GetTestList())); - -} // namespace media
diff --git a/media/gpu/android/avda_codec_image.cc b/media/gpu/android/avda_codec_image.cc deleted file mode 100644 index fd181d2..0000000 --- a/media/gpu/android/avda_codec_image.cc +++ /dev/null
@@ -1,260 +0,0 @@ -// Copyright 2015 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 "media/gpu/android/avda_codec_image.h" - -#include <string.h> - -#include <memory> - -#include "gpu/command_buffer/service/texture_manager.h" -#include "media/base/android/media_codec_bridge_impl.h" -#include "media/gpu/android/avda_shared_state.h" -#include "ui/gl/android/surface_texture.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/scoped_make_current.h" - -namespace media { - -AVDACodecImage::AVDACodecImage( - const scoped_refptr<AVDASharedState>& shared_state, - MediaCodecBridge* codec) - : shared_state_(shared_state), - codec_buffer_index_(kInvalidCodecBufferIndex), - media_codec_(codec), - has_texture_owner_(false), - texture_(0) {} - -AVDACodecImage::~AVDACodecImage() {} - -gfx::Size AVDACodecImage::GetSize() { - return size_; -} - -unsigned AVDACodecImage::GetInternalFormat() { - return GL_RGBA; -} - -AVDACodecImage::BindOrCopy AVDACodecImage::ShouldBindOrCopy() { - return COPY; -} - -bool AVDACodecImage::BindTexImage(unsigned target) { - NOTREACHED(); - return false; -} - -void AVDACodecImage::ReleaseTexImage(unsigned target) {} - -bool AVDACodecImage::CopyTexImage(unsigned target) { - if (!has_texture_owner_ || target != GL_TEXTURE_EXTERNAL_OES) - return false; - - GLint bound_service_id = 0; - glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id); - // We insist that the currently bound texture is the right one. - if (bound_service_id != - static_cast<GLint>(shared_state_->texture_owner_service_id())) { - return false; - } - - // Make sure that we have the right image in the front buffer. Note that the - // bound_service_id is guaranteed to be equal to the texture owner's client - // texture id, so we can skip preserving it if the right context is current. - UpdateSurfaceInternal(UpdateMode::RENDER_TO_FRONT_BUFFER, - kDontRestoreBindings); - - // By setting image state to UNBOUND instead of COPIED we ensure that - // CopyTexImage() is called each time the texture owner is used for drawing. - // It would be nice if we could do this via asking for the currently bound - // Texture, but the active unit never seems to change. - texture_->SetLevelImageState(GL_TEXTURE_EXTERNAL_OES, 0, - gpu::gles2::Texture::UNBOUND); - - return true; -} - -bool AVDACodecImage::CopyTexSubImage(unsigned target, - const gfx::Point& offset, - const gfx::Rect& rect) { - return false; -} - -bool AVDACodecImage::ScheduleOverlayPlane( - gfx::AcceleratedWidget widget, - int z_order, - gfx::OverlayTransform transform, - const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect, - bool enable_blend, - std::unique_ptr<gfx::GpuFence> gpu_fence) { - // This should only be called when we're rendering to a SurfaceView. - if (has_texture_owner_) { - DVLOG(1) << "Invalid call to ScheduleOverlayPlane; this image is " - "TextureOwner backed."; - return false; - } - - // Move the overlay if needed. - if (shared_state_->overlay() && most_recent_bounds_ != bounds_rect) { - most_recent_bounds_ = bounds_rect; - shared_state_->overlay()->ScheduleLayout(bounds_rect); - } - - UpdateSurface(UpdateMode::RENDER_TO_FRONT_BUFFER); - return true; -} - -void AVDACodecImage::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, - uint64_t process_tracing_id, - const std::string& dump_name) {} - -void AVDACodecImage::UpdateTextureOwner(RestoreBindingsMode mode) { - DCHECK(has_texture_owner_); - DCHECK_EQ(codec_buffer_index_, kUpdateOnly); - codec_buffer_index_ = kRendered; - - // Swap the rendered image to the front. - std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current = - MakeCurrentIfNeeded(); - - // If we changed contexts, then we always want to restore it, since the caller - // doesn't know that we're switching contexts. - if (scoped_make_current) - mode = kDoRestoreBindings; - - // Save the current binding if requested. - GLint bound_service_id = 0; - if (mode == kDoRestoreBindings) - glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id); - - shared_state_->UpdateTexImage(); - if (mode == kDoRestoreBindings) - glBindTexture(GL_TEXTURE_EXTERNAL_OES, bound_service_id); -} - -void AVDACodecImage::UpdateSurface(UpdateMode update_mode) { - UpdateSurfaceInternal(update_mode, kDoRestoreBindings); -} - -void AVDACodecImage::CodecChanged(MediaCodecBridge* codec) { - media_codec_ = codec; - codec_buffer_index_ = kInvalidCodecBufferIndex; -} - -void AVDACodecImage::SetBufferMetadata(int buffer_index, - bool has_texture_owner, - const gfx::Size& size) { - has_texture_owner_ = has_texture_owner; - codec_buffer_index_ = buffer_index; - size_ = size; -} - -bool AVDACodecImage::SetSharedState( - scoped_refptr<AVDASharedState> shared_state) { - if (shared_state == shared_state_) - return false; - shared_state_ = shared_state; - most_recent_bounds_ = gfx::Rect(); - return true; -} - -void AVDACodecImage::UpdateSurfaceInternal( - UpdateMode update_mode, - RestoreBindingsMode attached_bindings_mode) { - if (!IsCodecBufferOutstanding()) - return; - - ReleaseOutputBuffer(update_mode); - - // SurfaceViews are updated implicitly, so no further steps are necessary. - if (!has_texture_owner_) { - DCHECK(update_mode != UpdateMode::RENDER_TO_BACK_BUFFER); - return; - } - - // If front buffer rendering hasn't been requested, exit early. - if (update_mode != UpdateMode::RENDER_TO_FRONT_BUFFER) - return; - - UpdateTextureOwner(attached_bindings_mode); -} - -void AVDACodecImage::ReleaseOutputBuffer(UpdateMode update_mode) { - DCHECK(IsCodecBufferOutstanding()); - - // In case of discard, simply discard and clear our codec buffer index. - if (update_mode == UpdateMode::DISCARD_CODEC_BUFFER) { - if (codec_buffer_index_ != kUpdateOnly) - media_codec_->ReleaseOutputBuffer(codec_buffer_index_, false); - - // Note: No need to wait for the frame to be available in the kUpdateOnly - // case since it will be or has been waited on by another release call. - codec_buffer_index_ = kInvalidCodecBufferIndex; - return; - } - - DCHECK(update_mode == UpdateMode::RENDER_TO_BACK_BUFFER || - update_mode == UpdateMode::RENDER_TO_FRONT_BUFFER); - - if (!has_texture_owner_) { - DCHECK(update_mode == UpdateMode::RENDER_TO_FRONT_BUFFER); - DCHECK_GE(codec_buffer_index_, 0); - media_codec_->ReleaseOutputBuffer(codec_buffer_index_, true); - codec_buffer_index_ = kRendered; - return; - } - - // If we've already released to the back buffer, there's nothing left to do, - // but wait for the previously released buffer if necessary. - if (codec_buffer_index_ != kUpdateOnly) { - DCHECK(has_texture_owner_); - DCHECK_GE(codec_buffer_index_, 0); - shared_state_->RenderCodecBufferToTextureOwner(media_codec_, - codec_buffer_index_); - codec_buffer_index_ = kUpdateOnly; - } - - // Only wait for the TextureOwner update if we're rendering to the front. - if (update_mode == UpdateMode::RENDER_TO_FRONT_BUFFER) - shared_state_->WaitForFrameAvailable(); -} - -std::unique_ptr<ui::ScopedMakeCurrent> AVDACodecImage::MakeCurrentIfNeeded() { - DCHECK(shared_state_->context()); - // Remember: virtual contexts return true if and only if their shared context - // is current, regardless of which virtual context it is. - return std::unique_ptr<ui::ScopedMakeCurrent>( - shared_state_->context()->IsCurrent(nullptr) - ? nullptr - : new ui::ScopedMakeCurrent(shared_state_->context(), - shared_state_->surface())); -} - -void AVDACodecImage::GetTextureMatrix(float matrix[16]) { - // Our current matrix may be stale. Update it if possible. - if (has_texture_owner_) - UpdateSurface(UpdateMode::RENDER_TO_FRONT_BUFFER); - shared_state_->GetTransformMatrix(matrix); - YInvertMatrix(matrix); -} - -void AVDACodecImage::NotifyPromotionHint(bool promotion_hint, - int display_x, - int display_y, - int display_width, - int display_height) { - shared_state_->GetPromotionHintCB().Run(PromotionHintAggregator::Hint( - gfx::Rect(display_x, display_y, display_width, display_height), - promotion_hint)); -} - -bool AVDACodecImage::IsCodecBufferOutstanding() const { - static_assert(kUpdateOnly < 0 && kUpdateOnly > kRendered && - kRendered > kInvalidCodecBufferIndex, - "Codec buffer index enum values are not ordered correctly."); - return codec_buffer_index_ > kRendered && media_codec_; -} - -} // namespace media
diff --git a/media/gpu/android/avda_codec_image.h b/media/gpu/android/avda_codec_image.h deleted file mode 100644 index c3011e7c..0000000 --- a/media/gpu/android/avda_codec_image.h +++ /dev/null
@@ -1,168 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_GPU_ANDROID_AVDA_CODEC_IMAGE_H_ -#define MEDIA_GPU_ANDROID_AVDA_CODEC_IMAGE_H_ - -#include <stdint.h> - -#include <memory> - -#include "base/macros.h" -#include "gpu/command_buffer/service/gl_stream_texture_image.h" -#include "media/gpu/android/avda_shared_state.h" - -namespace ui { -class ScopedMakeCurrent; -} - -namespace media { - -class MediaCodecBridge; - -// GLImage that renders MediaCodec buffers to a TextureOwner or SurfaceView as -// needed in order to draw them. -class AVDACodecImage : public gpu::gles2::GLStreamTextureImage { - public: - AVDACodecImage(const scoped_refptr<AVDASharedState>& shared_state, - MediaCodecBridge* codec); - - // gl::GLImage implementation - gfx::Size GetSize() override; - unsigned GetInternalFormat() override; - BindOrCopy ShouldBindOrCopy() override; - bool BindTexImage(unsigned target) override; - void ReleaseTexImage(unsigned target) override; - bool CopyTexImage(unsigned target) override; - bool CopyTexSubImage(unsigned target, - const gfx::Point& offset, - const gfx::Rect& rect) override; - bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget, - int z_order, - gfx::OverlayTransform transform, - const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect, - bool enable_blend, - std::unique_ptr<gfx::GpuFence> gpu_fence) override; - void SetColorSpace(const gfx::ColorSpace& color_space) override {} - void Flush() override {} - void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, - uint64_t process_tracing_id, - const std::string& dump_name) override; - // gpu::gles2::GLStreamTextureMatrix implementation - void GetTextureMatrix(float xform[16]) override; - void NotifyPromotionHint(bool promotion_hint, - int display_x, - int display_y, - int display_width, - int display_height) override; - - enum class UpdateMode { - // Discards the codec buffer, no UpdateTexImage(). - DISCARD_CODEC_BUFFER, - - // Renders to back buffer, no UpdateTexImage(); can only be used with a - // valid |texture_owner_|. - RENDER_TO_BACK_BUFFER, - - // Renders to the back buffer. When used with a SurfaceView, promotion to - // the front buffer is automatic. When using a |texture_owner_|, - // UpdateTexImage() is called to promote the back buffer into the front. - RENDER_TO_FRONT_BUFFER - }; - - // Releases the attached codec buffer (if not already released) indicated by - // |codec_buffer_index_| and updates the surface if specified by the given - // |update_mode|. See UpdateMode documentation for details. - void UpdateSurface(UpdateMode update_mode); - - // Updates the MediaCodec for this image; clears |codec_buffer_index_|. - void CodecChanged(MediaCodecBridge* codec); - - void set_texture(gpu::gles2::Texture* texture) { texture_ = texture; } - - // Sets up the properties necessary for the image to render. |buffer_index| is - // supplied to ReleaseOutputBuffer(), |has_texture_owner| controls which - // rendering path is used, and |size| is used by the compositor. - void SetBufferMetadata(int buffer_index, - bool has_texture_owner, - const gfx::Size& size); - - bool SetSharedState(scoped_refptr<AVDASharedState> shared_state); - - // Indicates if the codec buffer has been released to the back buffer. - bool was_rendered_to_back_buffer() const { - return codec_buffer_index_ == kUpdateOnly; - } - - // Indicates if the codec buffer has been released to the front buffer. - bool was_rendered_to_front_buffer() const { - return codec_buffer_index_ == kRendered; - } - - bool is_unrendered() const { return codec_buffer_index_ >= kUpdateOnly; } - - protected: - ~AVDACodecImage() override; - - private: - // Make sure that the texture owner's front buffer is current. This will - // save / restore the current context. It will optionally restore the texture - // bindings in the texture owner's context, based on |mode|. This is - // intended as a hint if we don't need to change contexts. If we do need to - // change contexts, then we'll always preserve the texture bindings in the - // both contexts. In other words, the caller is telling us whether it's - // okay to change the binding in the current context. - enum RestoreBindingsMode { kDontRestoreBindings, kDoRestoreBindings }; - void UpdateTextureOwner(RestoreBindingsMode mode); - - // Internal helper for UpdateSurface() that allows callers to specify the - // RestoreBindingsMode when a TextureOwner is already attached prior to - // calling this method. - void UpdateSurfaceInternal(UpdateMode update_mode, - RestoreBindingsMode attached_bindings_mode); - - // Releases the attached codec buffer (if not already released) indicated by - // |codec_buffer_index_|. Never updates the actual surface. See UpdateMode - // documentation for details. For the purposes of this function the values - // RENDER_TO_FRONT_BUFFER and RENDER_TO_BACK_BUFFER do the same thing. - void ReleaseOutputBuffer(UpdateMode update_mode); - - // Make shared_state_->context() current if it isn't already. - std::unique_ptr<ui::ScopedMakeCurrent> MakeCurrentIfNeeded(); - - // Return whether there is a codec buffer that we haven't rendered yet. Will - // return false also if there's no codec or we otherwise can't update. - bool IsCodecBufferOutstanding() const; - - // Shared state between the AVDA and all AVDACodecImages. - scoped_refptr<AVDASharedState> shared_state_; - - // The MediaCodec buffer index that we should render. Must be >= 0 or one of - // the enum values below. - enum { kUpdateOnly = -1, kRendered = -2, kInvalidCodecBufferIndex = -3 }; - int codec_buffer_index_; - - // Our image size. - gfx::Size size_; - - // May be null. - MediaCodecBridge* media_codec_; - - // Indicates if we're rendering to a TextureOwner or not. Set during the - // call to SetBufferMetadata(). - bool has_texture_owner_; - - // The texture that we're attached to. - gpu::gles2::Texture* texture_; - - // Bounds that we last sent to our overlay. - gfx::Rect most_recent_bounds_; - - DISALLOW_COPY_AND_ASSIGN(AVDACodecImage); -}; - -} // namespace media - -#endif // MEDIA_GPU_ANDROID_AVDA_CODEC_IMAGE_H_
diff --git a/media/gpu/android/avda_picture_buffer_manager.cc b/media/gpu/android/avda_picture_buffer_manager.cc deleted file mode 100644 index 80ff9c4a..0000000 --- a/media/gpu/android/avda_picture_buffer_manager.cc +++ /dev/null
@@ -1,273 +0,0 @@ -// Copyright 2015 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 "media/gpu/android/avda_picture_buffer_manager.h" - -#include <EGL/egl.h> -#include <EGL/eglext.h> - -#include "base/android/build_info.h" -#include "base/bind.h" -#include "base/logging.h" -#include "base/metrics/histogram.h" -#include "base/stl_util.h" -#include "gpu/command_buffer/service/context_group.h" -#include "gpu/command_buffer/service/gl_stream_texture_image.h" -#include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h" -#include "gpu/command_buffer/service/texture_manager.h" -#include "gpu/ipc/service/gpu_channel.h" -#include "media/base/android/media_codec_bridge_impl.h" -#include "media/gpu/android/avda_codec_image.h" -#include "media/gpu/android/avda_shared_state.h" -#include "ui/gl/android/scoped_java_surface.h" -#include "ui/gl/android/surface_texture.h" -#include "ui/gl/egl_util.h" -#include "ui/gl/gl_bindings.h" -#include "ui/gl/gl_surface_egl.h" -#include "ui/gl/scoped_binders.h" -#include "ui/gl/scoped_make_current.h" - -// If !|ptr|, log a message, notify |state_provider_| of the error, and -// return an optional value. -#define RETURN_IF_NULL(ptr, ...) \ - do { \ - if (!(ptr)) { \ - DLOG(ERROR) << "Got null for " << #ptr; \ - state_provider_->NotifyError(VideoDecodeAccelerator::ILLEGAL_STATE); \ - return __VA_ARGS__; \ - } \ - } while (0) - -namespace media { - -AVDAPictureBufferManager::AVDAPictureBufferManager( - AVDAStateProvider* state_provider) - : state_provider_(state_provider), media_codec_(nullptr) {} - -AVDAPictureBufferManager::~AVDAPictureBufferManager() {} - -bool AVDAPictureBufferManager::Initialize( - scoped_refptr<AVDASurfaceBundle> surface_bundle) { - shared_state_ = nullptr; - texture_owner_ = nullptr; - - if (!surface_bundle->overlay) { - // Create the texture owner. - // TODO(liberato): Don't memorize this. However, since this entire path is - // deprecated, it's probably okay. - std::unique_ptr<gpu::gles2::AbstractTexture> texture = - state_provider_->CreateAbstractTexture(GL_TEXTURE_EXTERNAL_OES, GL_RGBA, - 0, // width, - 0, // height - 1, // depth - 0, // border - GL_RGBA, GL_UNSIGNED_BYTE); - texture_owner_ = TextureOwner::Create( - std::move(texture), TextureOwner::Mode::kSurfaceTextureInsecure); - if (!texture_owner_) - return false; - - surface_bundle->texture_owner_surface = texture_owner_->CreateJavaSurface(); - surface_bundle->texture_owner_ = texture_owner_; - } - - // Only do this once the texture owner is filled in, since the constructor - // assumes that it will be. - shared_state_ = new AVDASharedState(surface_bundle); - shared_state_->SetPromotionHintCB(state_provider_->GetPromotionHintCB()); - - return true; -} - -void AVDAPictureBufferManager::Destroy(const PictureBufferMap& buffers) { - // Do nothing if Initialize() has not been called. - if (!shared_state_) - return; - - ReleaseCodecBuffers(buffers); - CodecChanged(nullptr); - texture_owner_ = nullptr; -} - -void AVDAPictureBufferManager::SetImageForPicture( - const PictureBuffer& picture_buffer, - gpu::gles2::GLStreamTextureImage* image) { - auto* context_group = state_provider_->GetContextGroup(); - RETURN_IF_NULL(context_group); - auto* texture_manager = context_group->texture_manager(); - RETURN_IF_NULL(texture_manager); - - DCHECK_LE(1u, picture_buffer.client_texture_ids().size()); - gpu::gles2::TextureRef* texture_ref = - texture_manager->GetTexture(picture_buffer.client_texture_ids()[0]); - RETURN_IF_NULL(texture_ref); - - // Default to zero which will clear the stream texture service id if one was - // previously set. - GLuint stream_texture_service_id = 0; - if (image) { - // Override the Texture's service id, so that it will use the one that is - // attached to the TextureOwner - stream_texture_service_id = shared_state_->texture_owner_service_id(); - - // Also set the parameters for the level if we're not clearing the image. - const gfx::Size size = state_provider_->GetSize(); - texture_manager->SetLevelInfo(texture_ref, kTextureTarget, 0, GL_RGBA, - size.width(), size.height(), 1, 0, GL_RGBA, - GL_UNSIGNED_BYTE, gfx::Rect()); - - static_cast<AVDACodecImage*>(image)->set_texture(texture_ref->texture()); - } - - // If we're clearing the image, or setting a TextureOwner backed image, we - // set the state to UNBOUND. For TextureOwner images, this ensures that the - // implementation will call CopyTexImage, which is where AVDACodecImage - // updates the TextureOwner to the right frame. - auto image_state = gpu::gles2::Texture::UNBOUND; - // For SurfaceView we set the state to BOUND because ScheduleOverlayPlane - // requires it. If something tries to sample from this texture it won't work, - // but there's no way to sample from a SurfaceView anyway, so it doesn't - // matter. - if (image && !texture_owner_) - image_state = gpu::gles2::Texture::BOUND; - texture_manager->SetLevelStreamTextureImage(texture_ref, kTextureTarget, 0, - image, image_state, - stream_texture_service_id); - texture_manager->SetLevelCleared(texture_ref, kTextureTarget, 0, true); -} - -AVDACodecImage* AVDAPictureBufferManager::GetImageForPicture( - int picture_buffer_id) const { - auto it = codec_images_.find(picture_buffer_id); - DCHECK(it != codec_images_.end()); - return it->second.get(); -} - -void AVDAPictureBufferManager::UseCodecBufferForPictureBuffer( - int32_t codec_buf_index, - const PictureBuffer& picture_buffer) { - // Notify the AVDACodecImage for picture_buffer that it should use the - // decoded buffer codec_buf_index to render this frame. - AVDACodecImage* avda_image = GetImageForPicture(picture_buffer.id()); - - // Note that this is not a race, since we do not re-use a PictureBuffer - // until after the CC is done drawing it. - pictures_out_for_display_.push_back(picture_buffer.id()); - avda_image->SetBufferMetadata(codec_buf_index, !!texture_owner_, - state_provider_->GetSize()); - - // If the shared state has changed for this image, retarget its texture. - if (avda_image->SetSharedState(shared_state_)) - SetImageForPicture(picture_buffer, avda_image); - - MaybeRenderEarly(); -} - -void AVDAPictureBufferManager::AssignOnePictureBuffer( - const PictureBuffer& picture_buffer, - bool have_context) { - // Attach a GLImage to each texture that will use the texture owner. - scoped_refptr<gpu::gles2::GLStreamTextureImage> gl_image = - codec_images_[picture_buffer.id()] = - new AVDACodecImage(shared_state_, media_codec_); - SetImageForPicture(picture_buffer, gl_image.get()); -} - -void AVDAPictureBufferManager::ReleaseCodecBufferForPicture( - const PictureBuffer& picture_buffer) { - GetImageForPicture(picture_buffer.id()) - ->UpdateSurface(AVDACodecImage::UpdateMode::DISCARD_CODEC_BUFFER); -} - -void AVDAPictureBufferManager::ReuseOnePictureBuffer( - const PictureBuffer& picture_buffer) { - base::Erase(pictures_out_for_display_, picture_buffer.id()); - - // At this point, the CC must be done with the picture. We can't really - // check for that here directly. it's guaranteed in gpu_video_decoder.cc, - // when it waits on the sync point before releasing the mailbox. That sync - // point is inserted by destroying the resource in VideoLayerImpl::DidDraw. - ReleaseCodecBufferForPicture(picture_buffer); - MaybeRenderEarly(); -} - -void AVDAPictureBufferManager::ReleaseCodecBuffers( - const PictureBufferMap& buffers) { - for (const std::pair<int, PictureBuffer>& entry : buffers) - ReleaseCodecBufferForPicture(entry.second); -} - -void AVDAPictureBufferManager::MaybeRenderEarly() { - if (pictures_out_for_display_.empty()) - return; - - // See if we can consume the front buffer / render to the SurfaceView. Iterate - // in reverse to find the most recent front buffer. If none is found, the - // |front_index| will point to the beginning of the array. - size_t front_index = pictures_out_for_display_.size() - 1; - AVDACodecImage* first_renderable_image = nullptr; - for (int i = front_index; i >= 0; --i) { - const int id = pictures_out_for_display_[i]; - AVDACodecImage* avda_image = GetImageForPicture(id); - - // Update the front buffer index as we move along to shorten the number of - // candidate images we look at for back buffer rendering. - front_index = i; - first_renderable_image = avda_image; - - // If we find a front buffer, stop and indicate that front buffer rendering - // is not possible since another image is already in the front buffer. - if (avda_image->was_rendered_to_front_buffer()) { - first_renderable_image = nullptr; - break; - } - } - - if (first_renderable_image) { - first_renderable_image->UpdateSurface( - AVDACodecImage::UpdateMode::RENDER_TO_FRONT_BUFFER); - } - - // Back buffer rendering is only available for texture owners. We'll always - // have at least one front buffer, so the next buffer must be the backbuffer. - size_t backbuffer_index = front_index + 1; - if (!texture_owner_ || backbuffer_index >= pictures_out_for_display_.size()) - return; - - // See if the back buffer is free. If so, then render the frame adjacent to - // the front buffer. The listing is in render order, so we can just use the - // first unrendered frame if there is back buffer space. - first_renderable_image = - GetImageForPicture(pictures_out_for_display_[backbuffer_index]); - if (first_renderable_image->was_rendered_to_back_buffer()) - return; - - // Due to the loop in the beginning this should never be true. - DCHECK(!first_renderable_image->was_rendered_to_front_buffer()); - first_renderable_image->UpdateSurface( - AVDACodecImage::UpdateMode::RENDER_TO_BACK_BUFFER); -} - -void AVDAPictureBufferManager::CodecChanged(MediaCodecBridge* codec) { - media_codec_ = codec; - for (auto& image_kv : codec_images_) - image_kv.second->CodecChanged(codec); - shared_state_->ClearReleaseTime(); -} - -bool AVDAPictureBufferManager::ArePicturesOverlayable() { - // SurfaceView frames are always overlayable because that's the only way to - // display them. - return !texture_owner_; -} - -bool AVDAPictureBufferManager::HasUnrenderedPictures() const { - for (int id : pictures_out_for_display_) { - if (GetImageForPicture(id)->is_unrendered()) - return true; - } - return false; -} - -} // namespace media
diff --git a/media/gpu/android/avda_picture_buffer_manager.h b/media/gpu/android/avda_picture_buffer_manager.h deleted file mode 100644 index b93e324..0000000 --- a/media/gpu/android/avda_picture_buffer_manager.h +++ /dev/null
@@ -1,132 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_GPU_ANDROID_AVDA_PICTURE_BUFFER_MANAGER_H_ -#define MEDIA_GPU_ANDROID_AVDA_PICTURE_BUFFER_MANAGER_H_ - -#include <stdint.h> -#include <vector> - -#include "base/macros.h" -#include "media/gpu/android/avda_state_provider.h" -#include "media/gpu/android/avda_surface_bundle.h" -#include "media/gpu/android/surface_texture_gl_owner.h" -#include "media/gpu/media_gpu_export.h" - -namespace gpu { -namespace gles2 { -class GLStreamTextureImage; -} -} // namespace gpu - -namespace media { -class AVDACodecImage; -class AVDASharedState; -class MediaCodecBridge; - -// AVDAPictureBufferManager is used by AVDA to associate its PictureBuffers with -// MediaCodec output buffers. It attaches AVDACodecImages to the PictureBuffer -// textures so that when they're used to draw the AVDACodecImage can release the -// MediaCodec buffer to the backing Surface. If the Surface is a TextureOwner, -// the front buffer can then be used to draw without needing to copy the pixels. -// If the Surface is a SurfaceView, the release causes the frame to be displayed -// immediately. -class MEDIA_GPU_EXPORT AVDAPictureBufferManager { - public: - using PictureBufferMap = std::map<int32_t, PictureBuffer>; - - explicit AVDAPictureBufferManager(AVDAStateProvider* state_provider); - virtual ~AVDAPictureBufferManager(); - - // Call Initialize, providing the surface bundle that holds the surface that - // will back the frames. If an overlay is present in the bundle, then this - // will set us up to render codec buffers at the appropriate time for display, - // but will assume that consuming the resulting buffers is handled elsewhere - // (e.g., SurfaceFlinger). We will ensure that any reference to the bundle - // is dropped if the overlay sends OnSurfaceDestroyed. - // - // Without an overlay, we will create a TextureOwner and add it (and its - // surface) to |surface_bundle|. We will arrange to consume the buffers at - // the right time, in addition to releasing the codec buffers for rendering. - // - // One may call these multiple times to change between overlay and ST. - // - // Picture buffers will be updated to reflect the new surface during the call - // to UseCodecBufferForPicture(). - // - // Returns true on success. - bool Initialize(scoped_refptr<AVDASurfaceBundle> surface_bundle); - - void Destroy(const PictureBufferMap& buffers); - - // Sets up |picture_buffer| so that its texture will refer to the image that - // is represented by the decoded output buffer at codec_buffer_index. - void UseCodecBufferForPictureBuffer(int32_t codec_buffer_index, - const PictureBuffer& picture_buffer); - - // Assigns a picture buffer and attaches an image to its texture. - void AssignOnePictureBuffer(const PictureBuffer& picture_buffer, - bool have_context); - - // Reuses a picture buffer to hold a new frame. - void ReuseOnePictureBuffer(const PictureBuffer& picture_buffer); - - // Release MediaCodec buffers. - void ReleaseCodecBuffers(const PictureBufferMap& buffers); - - // Attempts to free up codec output buffers by rendering early. - void MaybeRenderEarly(); - - // Called when the MediaCodec instance changes. If |codec| is nullptr the - // MediaCodec is being destroyed. Previously provided codecs should no longer - // be referenced. - void CodecChanged(MediaCodecBridge* codec); - - // Whether the pictures buffers are overlayable. - bool ArePicturesOverlayable(); - - // Are there any unrendered picture buffers oustanding? - bool HasUnrenderedPictures() const; - - // Returns the GL texture target that the PictureBuffer textures use. - // Always use OES textures even though this will cause flickering in dev tools - // when inspecting a fullscreen video. See http://crbug.com/592798 - static constexpr GLenum kTextureTarget = GL_TEXTURE_EXTERNAL_OES; - - private: - // Release any codec buffer that is associated with the given picture buffer - // back to the codec. It is okay if there is no such buffer. - void ReleaseCodecBufferForPicture(const PictureBuffer& picture_buffer); - - // Sets up the texture references (as found by |picture_buffer|), for the - // specified |image|. If |image| is null, clears any ref on the texture - // associated with |picture_buffer|. - void SetImageForPicture(const PictureBuffer& picture_buffer, - gpu::gles2::GLStreamTextureImage* image); - - AVDACodecImage* GetImageForPicture(int picture_buffer_id) const; - - scoped_refptr<AVDASharedState> shared_state_; - - AVDAStateProvider* const state_provider_; - - // The texture owner to render to. Non-null after Initialize() if - // we're not rendering to a SurfaceView. - scoped_refptr<TextureOwner> texture_owner_; - - MediaCodecBridge* media_codec_; - - // Picture buffer IDs that are out for display. Stored in order of frames as - // they are returned from the decoder. - std::vector<int32_t> pictures_out_for_display_; - - // Maps a picture buffer id to a AVDACodecImage. - std::map<int, scoped_refptr<AVDACodecImage>> codec_images_; - - DISALLOW_COPY_AND_ASSIGN(AVDAPictureBufferManager); -}; - -} // namespace media - -#endif // MEDIA_GPU_ANDROID_AVDA_PICTURE_BUFFER_MANAGER_H_
diff --git a/media/gpu/android/avda_shared_state.cc b/media/gpu/android/avda_shared_state.cc deleted file mode 100644 index 2d5e1c1b..0000000 --- a/media/gpu/android/avda_shared_state.cc +++ /dev/null
@@ -1,81 +0,0 @@ -// Copyright 2015 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 "media/gpu/android/avda_shared_state.h" - -#include "base/bind.h" -#include "base/metrics/histogram_macros.h" -#include "base/time/time.h" -#include "media/gpu/android/avda_codec_image.h" -#include "ui/gl/android/surface_texture.h" -#include "ui/gl/gl_bindings.h" -#include "ui/gl/scoped_make_current.h" - -namespace media { - -AVDASharedState::AVDASharedState( - scoped_refptr<AVDASurfaceBundle> surface_bundle) - : gl_matrix_{ - 1, 0, 0, 0, // Default to a sane guess just in case we can't get the - 0, 1, 0, 0, // matrix on the first call. Will be Y-flipped later. - 0, 0, 1, 0, // - 0, 0, 0, 1, // Comment preserves 4x4 formatting. - }, - surface_bundle_(surface_bundle), - weak_this_factory_(this) { - // If we're holding a reference to an overlay, then register to drop it if the - // overlay's surface is destroyed. - if (overlay()) { - overlay()->AddSurfaceDestroyedCallback(base::Bind( - &AVDASharedState::ClearOverlay, weak_this_factory_.GetWeakPtr())); - } -} - -AVDASharedState::~AVDASharedState() = default; - -void AVDASharedState::RenderCodecBufferToTextureOwner(MediaCodecBridge* codec, - int codec_buffer_index) { - if (texture_owner()->IsExpectingFrameAvailable()) - texture_owner()->WaitForFrameAvailable(); - codec->ReleaseOutputBuffer(codec_buffer_index, true); - texture_owner()->SetReleaseTimeToNow(); -} - -void AVDASharedState::WaitForFrameAvailable() { - texture_owner()->WaitForFrameAvailable(); -} - -void AVDASharedState::UpdateTexImage() { - texture_owner()->UpdateTexImage(); - if (!texture_owner()->binds_texture_on_update()) - texture_owner()->EnsureTexImageBound(); - // Helpfully, this is already column major. - texture_owner()->GetTransformMatrix(gl_matrix_); -} - -void AVDASharedState::GetTransformMatrix(float matrix[16]) const { - memcpy(matrix, gl_matrix_, sizeof(gl_matrix_)); -} - -void AVDASharedState::ClearReleaseTime() { - if (texture_owner()) - texture_owner()->IgnorePendingRelease(); -} - -void AVDASharedState::ClearOverlay(AndroidOverlay* overlay_raw) { - if (surface_bundle_ && overlay() == overlay_raw) - surface_bundle_ = nullptr; -} - -void AVDASharedState::SetPromotionHintCB( - PromotionHintAggregator::NotifyPromotionHintCB cb) { - promotion_hint_cb_ = cb; -} - -const PromotionHintAggregator::NotifyPromotionHintCB& -AVDASharedState::GetPromotionHintCB() { - return promotion_hint_cb_; -} - -} // namespace media
diff --git a/media/gpu/android/avda_shared_state.h b/media/gpu/android/avda_shared_state.h deleted file mode 100644 index 954f775..0000000 --- a/media/gpu/android/avda_shared_state.h +++ /dev/null
@@ -1,113 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_GPU_ANDROID_AVDA_SHARED_STATE_H_ -#define MEDIA_GPU_ANDROID_AVDA_SHARED_STATE_H_ - -#include "base/memory/weak_ptr.h" -#include "base/synchronization/waitable_event.h" -#include "gpu/command_buffer/service/gles2_cmd_decoder.h" -#include "media/base/android/android_overlay.h" -#include "media/base/android/media_codec_bridge.h" -#include "media/gpu/android/avda_shared_state.h" -#include "media/gpu/android/avda_surface_bundle.h" -#include "media/gpu/android/promotion_hint_aggregator.h" -#include "ui/gl/gl_bindings.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_image.h" -#include "ui/gl/gl_surface.h" - -namespace media { - -// State shared by AVDACodecImages. This holds a reference to the surface -// bundle that's backing the frames. If it's an overlay, then we'll -// automatically drop our reference to the bundle if the overlay's surface gets -// an OnSurfaceDestroyed. -// TODO(watk): This doesn't really do anything any more; we should delete it. -class AVDASharedState : public base::RefCounted<AVDASharedState> { - public: - AVDASharedState(scoped_refptr<AVDASurfaceBundle> surface_bundle); - - GLuint texture_owner_service_id() const { - return texture_owner() ? texture_owner()->GetTextureId() : 0; - } - - TextureOwner* texture_owner() const { - return surface_bundle_ ? surface_bundle_->texture_owner_.get() : nullptr; - } - - AndroidOverlay* overlay() const { - return surface_bundle_ ? surface_bundle_->overlay.get() : nullptr; - } - - // Context and surface that |texture_owner_| is bound to, if - // |texture_owner_| is not null. - gl::GLContext* context() const { - return texture_owner() ? texture_owner()->GetContext() : nullptr; - } - - gl::GLSurface* surface() const { - return texture_owner() ? texture_owner()->GetSurface() : nullptr; - } - - // Helper method for coordinating the interactions between - // MediaCodec::ReleaseOutputBuffer() and WaitForFrameAvailable() when - // rendering to a TextureOwner; this method should never be called when - // rendering to a SurfaceView. - // - // The release of the codec buffer to the texture owner is asynchronous, by - // using this helper we can attempt to let this process complete in a non - // blocking fashion before the TextureOwner is used. - // - // Clients should call this method to release the codec buffer for rendering - // and then call WaitForFrameAvailable() before using the TextureOwner. In - // the ideal case the TextureOwner has already been updated, otherwise the - // method will wait for a pro-rated amount of time based on elapsed time up - // to a short deadline. - // - // Some devices do not reliably notify frame availability, so we use a very - // short deadline of only a few milliseconds to avoid indefinite stalls. - void RenderCodecBufferToTextureOwner(MediaCodecBridge* codec, - int codec_buffer_index); - - void WaitForFrameAvailable(); - - // Helper methods for interacting with |texture_owner_|. See - // gl::TextureOwner for method details. - void UpdateTexImage(); - - // Returns a matrix that needs to be y flipped in order to match the - // StreamTextureMatrix contract. See GLStreamTextureImage::YInvertMatrix(). - void GetTransformMatrix(float matrix[16]) const; - - // Resets the last time for RenderCodecBufferToTextureOwner(). Should be - // called during codec changes. - void ClearReleaseTime(); - - void ClearOverlay(AndroidOverlay* overlay); - - void SetPromotionHintCB(PromotionHintAggregator::NotifyPromotionHintCB cb); - const PromotionHintAggregator::NotifyPromotionHintCB& GetPromotionHintCB(); - - protected: - virtual ~AVDASharedState(); - - private: - friend class base::RefCounted<AVDASharedState>; - - // Texture matrix of the front buffer of the texture owner. - float gl_matrix_[16]; - - scoped_refptr<AVDASurfaceBundle> surface_bundle_; - - PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb_; - - base::WeakPtrFactory<AVDASharedState> weak_this_factory_; - - DISALLOW_COPY_AND_ASSIGN(AVDASharedState); -}; - -} // namespace media - -#endif // MEDIA_GPU_ANDROID_AVDA_SHARED_STATE_H_
diff --git a/media/gpu/android/avda_state_provider.h b/media/gpu/android/avda_state_provider.h deleted file mode 100644 index 4ae1dde..0000000 --- a/media/gpu/android/avda_state_provider.h +++ /dev/null
@@ -1,53 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_GPU_ANDROID_AVDA_STATE_PROVIDER_H_ -#define MEDIA_GPU_ANDROID_AVDA_STATE_PROVIDER_H_ - -#include "base/compiler_specific.h" -#include "base/threading/thread_checker.h" -#include "gpu/command_buffer/service/texture_manager.h" -#include "media/gpu/android/promotion_hint_aggregator.h" -#include "media/video/video_decode_accelerator.h" - -namespace gpu { -namespace gles2 { -class ContextGroup; -} -} // namespace gpu - -namespace media { - -// Helper class that provides AVDAPictureBufferManager with enough state -// to do useful work. -class AVDAStateProvider { - public: - // Various handy getters. - virtual const gfx::Size& GetSize() const = 0; - virtual gpu::gles2::ContextGroup* GetContextGroup() const = 0; - virtual std::unique_ptr<gpu::gles2::AbstractTexture> CreateAbstractTexture( - GLenum target, - GLenum internal_format, - GLsizei width, - GLsizei height, - GLsizei depth, - int border, - GLenum format, - GLenum type) = 0; - - // Report a fatal error. This will post NotifyError(), and transition to the - // error state. - virtual void NotifyError(VideoDecodeAccelerator::Error error) = 0; - - // Return a callback that may be used to signal promotion hint info. - virtual PromotionHintAggregator::NotifyPromotionHintCB - GetPromotionHintCB() = 0; - - protected: - ~AVDAStateProvider() = default; -}; - -} // namespace media - -#endif // MEDIA_GPU_ANDROID_AVDA_STATE_PROVIDER_H_
diff --git a/media/gpu/android/codec_allocator.cc b/media/gpu/android/codec_allocator.cc index ef5f3c3..5d3660d 100644 --- a/media/gpu/android/codec_allocator.cc +++ b/media/gpu/android/codec_allocator.cc
@@ -23,7 +23,6 @@ #include "media/base/limits.h" #include "media/base/media.h" #include "media/base/timestamp_constants.h" -#include "media/gpu/android/android_video_decode_accelerator.h" namespace media {
diff --git a/media/gpu/gpu_video_decode_accelerator_factory.cc b/media/gpu/gpu_video_decode_accelerator_factory.cc index dc9ff3f..f9787be 100644 --- a/media/gpu/gpu_video_decode_accelerator_factory.cc +++ b/media/gpu/gpu_video_decode_accelerator_factory.cc
@@ -28,12 +28,6 @@ #include "media/gpu/v4l2/v4l2_video_decode_accelerator.h" #include "ui/gl/gl_surface_egl.h" #endif -#if defined(OS_ANDROID) -#include "media/gpu/android/android_video_decode_accelerator.h" -#include "media/gpu/android/android_video_surface_chooser_impl.h" -#include "media/gpu/android/codec_allocator.h" -#include "media/gpu/android/device_info.h" -#endif #if BUILDFLAG(USE_VAAPI) #include "media/gpu/vaapi/vaapi_video_decode_accelerator.h" #include "ui/gl/gl_implementation.h" @@ -79,9 +73,6 @@ #elif defined(OS_MACOSX) capabilities.supported_profiles = VTVideoDecodeAccelerator::GetSupportedProfiles(); -#elif defined(OS_ANDROID) - capabilities = - AndroidVideoDecodeAccelerator::GetCapabilities(gpu_preferences); #endif return GpuVideoAcceleratorUtil::ConvertMediaToGpuDecodeCapabilities( @@ -172,9 +163,6 @@ #if defined(OS_MACOSX) &GpuVideoDecodeAcceleratorFactory::CreateVTVDA, #endif -#if defined(OS_ANDROID) - &GpuVideoDecodeAcceleratorFactory::CreateAndroidVDA, -#endif }; std::unique_ptr<VideoDecodeAccelerator> vda; @@ -260,23 +248,6 @@ } #endif -#if defined(OS_ANDROID) -std::unique_ptr<VideoDecodeAccelerator> -GpuVideoDecodeAcceleratorFactory::CreateAndroidVDA( - const gpu::GpuDriverBugWorkarounds& workarounds, - const gpu::GpuPreferences& gpu_preferences, - MediaLog* media_log) const { - std::unique_ptr<VideoDecodeAccelerator> decoder; - decoder.reset(new AndroidVideoDecodeAccelerator( - CodecAllocator::GetInstance(base::ThreadTaskRunnerHandle::Get()), - std::make_unique<AndroidVideoSurfaceChooserImpl>( - DeviceInfo::GetInstance()->IsSetOutputSurfaceSupported()), - make_context_current_cb_, get_context_group_cb_, overlay_factory_cb_, - create_abstract_texture_cb_, DeviceInfo::GetInstance())); - return decoder; -} -#endif - GpuVideoDecodeAcceleratorFactory::GpuVideoDecodeAcceleratorFactory( const GetGLContextCallback& get_gl_context_cb, const MakeGLContextCurrentCallback& make_context_current_cb,
diff --git a/media/mojo/README.md b/media/mojo/README.md index 8e3e9bb..652e62be 100644 --- a/media/mojo/README.md +++ b/media/mojo/README.md
@@ -200,7 +200,6 @@ 2. Remote media components hosted in `MediaService`, e.g. by `MojoRendererService`, `MojoAudioDecoderService` and `MojoVideoDecoderService`. -3. Legacy remote media components like `AndroidVideoDecodeAccelerator`. At the JavaScript layer, the media player and MediaKeys are connected via [`setMediaKeys()`](https://w3c.github.io/encrypted-media/#dom-htmlmediaelement-setmediakeys). @@ -223,13 +222,12 @@ #### Using CdmContext In some cases the media component is set to work with a specific CDM. For -example, on Android, MediaCodec-based decoders (e.g. `MediaCodecAudioDecoder` -and `AndroidVideoDecodeAccelerator`) can only use MediaDrm-based CDM via -`MediaCryptoContext`. The media component and the CDM must live in the same -process because the interaction of these two are typically happening deep at the -OS level. In theory, they can both live in the render process. But in practice, -typically both the CDM and the media component are hosted by the MediaService in -a remote (e.g. GPU) process. +example, on Android, MediaCodec-based decoders (e.g. `MediaCodecAudioDecoder`) +can only use MediaDrm-based CDM via `MediaCryptoContext`. The media component +and the CDM must live in the same process because the interaction of these two +are typically happening deep at the OS level. In theory, they can both live in +the render process. But in practice, typically both the CDM and the media +component are hosted by the MediaService in a remote (e.g. GPU) process. To be able to attach a remote CDM with a remote media component, each `InterfaceFactoryImpl` instance (corresponding to one `RenderFrame`) in the
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn index d43ba94..f6f8fc6 100644 --- a/media/mojo/services/BUILD.gn +++ b/media/mojo/services/BUILD.gn
@@ -91,7 +91,6 @@ "//gpu/ipc/service", "//media", "//media:shared_memory_support", - "//media/cdm:cdm_manager", "//media/gpu", "//media/gpu:buildflags", "//media/gpu/ipc/service",
diff --git a/media/mojo/services/mojo_cdm_service.cc b/media/mojo/services/mojo_cdm_service.cc index 1ccfd2f..e4ce751 100644 --- a/media/mojo/services/mojo_cdm_service.cc +++ b/media/mojo/services/mojo_cdm_service.cc
@@ -16,7 +16,6 @@ #include "media/base/cdm_factory.h" #include "media/base/cdm_key_information.h" #include "media/base/key_systems.h" -#include "media/cdm/cdm_manager.h" #include "media/mojo/common/media_type_converters.h" #include "media/mojo/services/mojo_cdm_service_context.h" #include "url/origin.h" @@ -49,7 +48,6 @@ if (!context_ || cdm_id_ == CdmContext::kInvalidCdmId) return; - CdmManager::GetInstance()->UnregisterCdm(cdm_id_); context_->UnregisterCdm(cdm_id_); } @@ -161,7 +159,6 @@ if (context_) { cdm_id_ = context_->RegisterCdm(this); - CdmManager::GetInstance()->RegisterCdm(cdm_id_, cdm); DVLOG(1) << __func__ << ": CDM successfully registered with ID " << cdm_id_; }
diff --git a/media/remoting/media_remoting_rpc.proto b/media/remoting/media_remoting_rpc.proto index 66d7db5..ebf5881 100644 --- a/media/remoting/media_remoting_rpc.proto +++ b/media/remoting/media_remoting_rpc.proto
@@ -189,6 +189,8 @@ AV1PROFILE_PROFILE_MAIN = 24; AV1PROFILE_PROFILE_HIGH = 25; AV1PROFILE_PROFILE_PRO = 26; + DOLBYVISION_PROFILE8 = 27; + DOLBYVISION_PROFILE9 = 28; }; // Proto version of media::VideoPixelFormat.
diff --git a/media/remoting/proto_enum_utils.cc b/media/remoting/proto_enum_utils.cc index 9f41d7f9..f592da04 100644 --- a/media/remoting/proto_enum_utils.cc +++ b/media/remoting/proto_enum_utils.cc
@@ -284,6 +284,8 @@ CASE_RETURN_OTHER(DOLBYVISION_PROFILE4); CASE_RETURN_OTHER(DOLBYVISION_PROFILE5); CASE_RETURN_OTHER(DOLBYVISION_PROFILE7); + CASE_RETURN_OTHER(DOLBYVISION_PROFILE8); + CASE_RETURN_OTHER(DOLBYVISION_PROFILE9); CASE_RETURN_OTHER(THEORAPROFILE_ANY); CASE_RETURN_OTHER(AV1PROFILE_PROFILE_MAIN); CASE_RETURN_OTHER(AV1PROFILE_PROFILE_HIGH); @@ -321,6 +323,8 @@ CASE_RETURN_OTHER(DOLBYVISION_PROFILE4); CASE_RETURN_OTHER(DOLBYVISION_PROFILE5); CASE_RETURN_OTHER(DOLBYVISION_PROFILE7); + CASE_RETURN_OTHER(DOLBYVISION_PROFILE8); + CASE_RETURN_OTHER(DOLBYVISION_PROFILE9); CASE_RETURN_OTHER(THEORAPROFILE_ANY); CASE_RETURN_OTHER(AV1PROFILE_PROFILE_MAIN); CASE_RETURN_OTHER(AV1PROFILE_PROFILE_HIGH);
diff --git a/mojo/core/channel.cc b/mojo/core/channel.cc index 7daaaa1..14ad9aa 100644 --- a/mojo/core/channel.cc +++ b/mojo/core/channel.cc
@@ -247,6 +247,11 @@ sizeof(MachPortsEntry); #else const uint32_t max_handles = 0; + // No extra header expected. Fail if this is detected. + if (extra_header_size > 0) { + DLOG(ERROR) << "Decoding invalid message: unexpected extra_header_size > 0"; + return nullptr; + } #endif // defined(OS_WIN) const uint16_t num_handles =
diff --git a/mojo/core/channel_unittest.cc b/mojo/core/channel_unittest.cc index 14585e3..14db631 100644 --- a/mojo/core/channel_unittest.cc +++ b/mojo/core/channel_unittest.cc
@@ -13,6 +13,7 @@ #include "base/process/process_handle.h" #include "base/run_loop.h" #include "base/threading/thread.h" +#include "build/build_config.h" #include "mojo/core/platform_handle_utils.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "testing/gmock/include/gmock/gmock.h" @@ -364,6 +365,28 @@ base::kNullProcessHandle)); } +#if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_FUCHSIA) +TEST(ChannelTest, DeserializeMessage_NonZeroExtraHeaderSize) { + // Verifies that a message payload is rejected when the extra header chunk + // size anything but zero on Linux, even if it's aligned. + constexpr uint16_t kTotalHeaderSize = + sizeof(Channel::Message::Header) + kChannelMessageAlignment; + constexpr uint32_t kEmptyPayloadSize = 8; + constexpr uint32_t kMessageSize = kTotalHeaderSize + kEmptyPayloadSize; + char message[kMessageSize]; + memset(message, 0, kMessageSize); + + Channel::Message::Header* header = + reinterpret_cast<Channel::Message::Header*>(&message[0]); + header->num_bytes = kMessageSize; + header->num_header_bytes = kTotalHeaderSize; + header->message_type = Channel::Message::MessageType::NORMAL; + header->num_handles = 0; + EXPECT_EQ(nullptr, Channel::Message::Deserialize(&message[0], kMessageSize, + base::kNullProcessHandle)); +} +#endif + class CountingChannelDelegate : public Channel::Delegate { public: explicit CountingChannelDelegate(base::OnceClosure on_final_message)
diff --git a/net/BUILD.gn b/net/BUILD.gn index b554414..edfcdc1 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -2664,8 +2664,6 @@ "data/ssl/certificates/treadclimber.sctlist", "data/ssl/certificates/unescaped.pem", "data/ssl/certificates/unittest.key.bin", - "data/ssl/certificates/unittest.originbound.der", - "data/ssl/certificates/unittest.originbound.key.der", "data/ssl/certificates/unittest.selfsigned.der", "data/ssl/certificates/verisign_class3_g5_crosssigned-trusted.keychain", "data/ssl/certificates/verisign_class3_g5_crosssigned.pem", @@ -6351,6 +6349,19 @@ ] } +fuzzer_test("net_crl_set_fuzzer") { + sources = [ + "cert/crl_set_fuzzer.cc", + ] + deps = [ + ":net_fuzzer_test_support", + ":test_support", + "//base", + "//net", + ] + seed_corpus = "data/fuzzer_data/net_crl_set_fuzzer/" +} + if (!disable_ftp_support) { fuzzer_test("net_ftp_ctrl_response_fuzzer") { sources = [
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc index 484db21..8eb68e8b 100644 --- a/net/cert/cert_verify_proc_unittest.cc +++ b/net/cert/cert_verify_proc_unittest.cc
@@ -40,6 +40,7 @@ #include "net/cert/x509_certificate.h" #include "net/cert/x509_util.h" #include "net/cert_net/cert_net_fetcher_impl.h" +#include "net/der/encode_values.h" #include "net/der/input.h" #include "net/der/parser.h" #include "net/proxy_resolution/proxy_config.h" @@ -177,6 +178,15 @@ false; #endif +// Wrapper for base::mac::IsAtLeastOS10_12() to avoid littering ifdefs. +bool IsMacAtLeastOS10_12() { +#if defined(OS_MACOSX) && !defined(OS_IOS) + return base::mac::IsAtLeastOS10_12(); +#else + return false; +#endif +} + // Returns a textual description of the CertVerifyProc implementation // that is being tested, used to give better names to parameterized // tests. @@ -238,6 +248,16 @@ #endif } +// Helper to make creating an X509Certificate chain less verbose. +scoped_refptr<X509Certificate> CreateX509CertificateWithIntermediate( + bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer, + bssl::UniquePtr<CRYPTO_BUFFER> intermediate_buffer) { + std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates; + intermediates.push_back(std::move(intermediate_buffer)); + return X509Certificate::CreateFromBuffer(std::move(cert_buffer), + std::move(intermediates)); +} + std::string MakeRandomHexString(size_t num_bytes) { std::vector<char> rand_bytes; rand_bytes.resize(num_bytes); @@ -246,6 +266,65 @@ return base::HexEncode(&rand_bytes[0], rand_bytes.size()); } +std::string Sha256WithRSAEncryption() { + const uint8_t kSha256WithRSAEncryption[] = {0x30, 0x0D, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00}; + return std::string(std::begin(kSha256WithRSAEncryption), + std::end(kSha256WithRSAEncryption)); +} + +// Adds bytes (specified as a StringPiece) to the given CBB. +// The argument ordering follows the boringssl CBB_* api style. +bool CBBAddBytes(CBB* cbb, base::StringPiece bytes) { + return CBB_add_bytes(cbb, reinterpret_cast<const uint8_t*>(bytes.data()), + bytes.size()); +} + +// Adds bytes (from fixed size array) to the given CBB. +// The argument ordering follows the boringssl CBB_* api style. +template <size_t N> +bool CBBAddBytes(CBB* cbb, const uint8_t (&data)[N]) { + return CBB_add_bytes(cbb, data, N); +} + +// Adds a RFC 5280 Time value to the given CBB. +// The argument ordering follows the boringssl CBB_* api style. +bool CBBAddTime(CBB* cbb, const base::Time& time) { + der::GeneralizedTime generalized_time; + if (!der::EncodeTimeAsGeneralizedTime(time, &generalized_time)) + return false; + CBB time_cbb; + if (generalized_time.year < 2050) { + uint8_t out[der::kUTCTimeLength]; + if (!der::EncodeUTCTime(generalized_time, out) || + !CBB_add_asn1(cbb, &time_cbb, CBS_ASN1_UTCTIME) || + !CBBAddBytes(&time_cbb, out) || !CBB_flush(cbb)) + return false; + } else { + uint8_t out[der::kGeneralizedTimeLength]; + if (!der::EncodeGeneralizedTime(generalized_time, out) || + !CBB_add_asn1(cbb, &time_cbb, CBS_ASN1_GENERALIZEDTIME) || + !CBBAddBytes(&time_cbb, out) || !CBB_flush(cbb)) + return false; + } + return true; +} + +// Finalizes the CBB to a std::string. +std::string FinishCBB(CBB* cbb) { + size_t cbb_len; + uint8_t* cbb_bytes; + + if (!CBB_finish(cbb, &cbb_bytes, &cbb_len)) { + ADD_FAILURE() << "CBB_finish() failed"; + return std::string(); + } + + bssl::UniquePtr<uint8_t> delete_bytes(cbb_bytes); + return std::string(reinterpret_cast<char*>(cbb_bytes), cbb_len); +} + // CertBuilder is a helper class to dynamically create a test certificate. // // CertBuilder is initialized using an existing certificate, from which it @@ -279,6 +358,13 @@ Invalidate(); } + // Removes an extension (if present). + void EraseExtension(const der::Input& oid) { + extensions_.erase(oid.AsString()); + + Invalidate(); + } + // Sets an AIA extension with a single caIssuers access method. void SetCaIssuersUrl(const GURL& url) { std::string url_spec = url.spec(); @@ -297,15 +383,49 @@ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &aia, CBS_ASN1_SEQUENCE)); ASSERT_TRUE(CBB_add_asn1(&aia, &ca_issuer, CBS_ASN1_SEQUENCE)); ASSERT_TRUE(CBB_add_asn1(&ca_issuer, &access_method, CBS_ASN1_OBJECT)); - ASSERT_TRUE( - AddBytesToCBB(&access_method, AdCaIssuersOid().AsStringPiece())); + ASSERT_TRUE(CBBAddBytes(&access_method, AdCaIssuersOid().AsStringPiece())); ASSERT_TRUE(CBB_add_asn1(&ca_issuer, &access_location, CBS_ASN1_CONTEXT_SPECIFIC | 6)); - ASSERT_TRUE(AddBytesToCBB(&access_location, url_spec)); + ASSERT_TRUE(CBBAddBytes(&access_location, url_spec)); SetExtension(AuthorityInfoAccessOid(), FinishCBB(cbb.get())); } + void SetCrlDistributionPointUrl(const GURL& url) { + std::string url_spec = url.spec(); + + bssl::ScopedCBB cbb; + ASSERT_TRUE(CBB_init(cbb.get(), url_spec.size())); + CBB dps, dp, dp_name, dp_fullname, dp_url; + + // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + ASSERT_TRUE(CBB_add_asn1(cbb.get(), &dps, CBS_ASN1_SEQUENCE)); + + // DistributionPoint ::= SEQUENCE { + // distributionPoint [0] DistributionPointName OPTIONAL, + // reasons [1] ReasonFlags OPTIONAL, + // cRLIssuer [2] GeneralNames OPTIONAL } + ASSERT_TRUE(CBB_add_asn1(&dps, &dp, CBS_ASN1_SEQUENCE)); + ASSERT_TRUE(CBB_add_asn1( + &dp, &dp_name, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)); + + // DistributionPointName ::= CHOICE { + // fullName [0] GeneralNames, + // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + ASSERT_TRUE( + CBB_add_asn1(&dp_name, &dp_fullname, + CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)); + + // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + // GeneralName ::= CHOICE { + // uniformResourceIdentifier [6] IA5String, + ASSERT_TRUE( + CBB_add_asn1(&dp_fullname, &dp_url, CBS_ASN1_CONTEXT_SPECIFIC | 6)); + ASSERT_TRUE(CBBAddBytes(&dp_url, url_spec)); + + SetExtension(CrlDistributionPointsOid(), FinishCBB(cbb.get())); + } + // Sets the SAN for the certificate to a single dNSName. void SetSubjectAltName(const std::string& dns_name) { // From RFC 5280: @@ -325,7 +445,7 @@ ASSERT_TRUE(CBB_add_asn1(cbb.get(), &general_names, CBS_ASN1_SEQUENCE)); ASSERT_TRUE(CBB_add_asn1(&general_names, &general_name, CBS_ASN1_CONTEXT_SPECIFIC | 2)); - ASSERT_TRUE(AddBytesToCBB(&general_name, dns_name)); + ASSERT_TRUE(CBBAddBytes(&general_name, dns_name)); SetExtension(SubjectAltNameOid(), FinishCBB(cbb.get())); } @@ -335,11 +455,7 @@ void SetSignatureAlgorithmRsaPkca1(DigestAlgorithm digest) { switch (digest) { case DigestAlgorithm::Sha256: { - const uint8_t kSha256WithRSAEncryption[] = { - 0x30, 0x0D, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00}; - SetSignatureAlgorithm(std::string(std::begin(kSha256WithRSAEncryption), - std::end(kSha256WithRSAEncryption))); + SetSignatureAlgorithm(Sha256WithRSAEncryption()); break; } @@ -385,6 +501,13 @@ return subject_tlv_; } + // Returns the serial number for the generated certificate. + uint64_t GetSerialNumber() { + if (!serial_number_) + serial_number_ = base::RandUint64(); + return serial_number_; + } + // Returns the (RSA) key for the generated certificate. EVP_PKEY* GetKey() { if (!key_) @@ -415,26 +538,6 @@ key_ = bssl::UpRef(private_key->key()); } - // Adds bytes (specified as a StringPiece) to the given CBB. - static bool AddBytesToCBB(CBB* cbb, base::StringPiece bytes) { - return CBB_add_bytes(cbb, reinterpret_cast<const uint8_t*>(bytes.data()), - bytes.size()); - } - - // Finalizes the CBB to a std::string. - static std::string FinishCBB(CBB* cbb) { - size_t cbb_len; - uint8_t* cbb_bytes; - - if (!CBB_finish(cbb, &cbb_bytes, &cbb_len)) { - ADD_FAILURE() << "CBB_finish() failed"; - return std::string(); - } - - bssl::UniquePtr<uint8_t> delete_bytes(cbb_bytes); - return std::string(reinterpret_cast<char*>(cbb_bytes), cbb_len); - } - // Generates a random subject for the certificate, comprised of just a CN. void GenerateSubject() { ASSERT_TRUE(subject_tlv_.empty()); @@ -453,20 +556,13 @@ ASSERT_TRUE(CBB_add_asn1(&rdns, &rdn, CBS_ASN1_SET)); ASSERT_TRUE(CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE)); ASSERT_TRUE(CBB_add_asn1(&attr, &type, CBS_ASN1_OBJECT)); - ASSERT_TRUE(CBB_add_bytes(&type, kCommonName, sizeof(kCommonName))); + ASSERT_TRUE(CBBAddBytes(&type, kCommonName)); ASSERT_TRUE(CBB_add_asn1(&attr, &value, CBS_ASN1_UTF8STRING)); - ASSERT_TRUE(AddBytesToCBB(&value, common_name)); + ASSERT_TRUE(CBBAddBytes(&value, common_name)); subject_tlv_ = FinishCBB(cbb.get()); } - // Returns the serial number for the generated certificate. - uint64_t GetSerialNumber() { - if (!serial_number_) - serial_number_ = base::RandUint64(); - return serial_number_; - } - // Parses |cert| and copies the following properties: // * All extensions (dropping any duplicates) // * Signature algorithm (from Certificate) @@ -564,9 +660,9 @@ ASSERT_TRUE(CBB_add_asn1_uint64(&version, 2)); ASSERT_TRUE(CBB_add_asn1_uint64(&tbs_cert, GetSerialNumber())); ASSERT_TRUE(AddSignatureAlgorithm(&tbs_cert)); - ASSERT_TRUE(AddBytesToCBB(&tbs_cert, issuer_->GetSubject())); - ASSERT_TRUE(AddBytesToCBB(&tbs_cert, validity_tlv_)); - ASSERT_TRUE(AddBytesToCBB(&tbs_cert, GetSubject())); + ASSERT_TRUE(CBBAddBytes(&tbs_cert, issuer_->GetSubject())); + ASSERT_TRUE(CBBAddBytes(&tbs_cert, validity_tlv_)); + ASSERT_TRUE(CBBAddBytes(&tbs_cert, GetSubject())); ASSERT_TRUE(EVP_marshal_public_key(&tbs_cert, GetKey())); // Serialize all the extensions. @@ -590,14 +686,14 @@ ASSERT_TRUE( CBB_add_asn1(&extensions, &extension_seq, CBS_ASN1_SEQUENCE)); ASSERT_TRUE(CBB_add_asn1(&extension_seq, &oid, CBS_ASN1_OBJECT)); - ASSERT_TRUE(AddBytesToCBB(&oid, extension_it.first)); + ASSERT_TRUE(CBBAddBytes(&oid, extension_it.first)); if (extension_it.second.critical) { ASSERT_TRUE(CBB_add_asn1_bool(&extension_seq, true)); } ASSERT_TRUE( CBB_add_asn1(&extension_seq, &extn_value, CBS_ASN1_OCTETSTRING)); - ASSERT_TRUE(AddBytesToCBB(&extn_value, extension_it.second.value)); + ASSERT_TRUE(CBBAddBytes(&extn_value, extension_it.second.value)); ASSERT_TRUE(CBB_flush(&extensions)); } } @@ -606,7 +702,7 @@ } bool AddSignatureAlgorithm(CBB* cbb) { - return AddBytesToCBB(cbb, signature_algorithm_tlv_); + return CBBAddBytes(cbb, signature_algorithm_tlv_); } void GenerateCertificate() { @@ -649,7 +745,7 @@ ASSERT_TRUE(CBB_init(cbb.get(), tbs_cert.size())); ASSERT_TRUE(CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE)); - ASSERT_TRUE(AddBytesToCBB(&cert, tbs_cert)); + ASSERT_TRUE(CBBAddBytes(&cert, tbs_cert)); ASSERT_TRUE(AddSignatureAlgorithm(&cert)); ASSERT_TRUE(CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING)); ASSERT_TRUE(CBB_add_u8(&signature, 0 /* no unused bits */)); @@ -773,6 +869,19 @@ verify_proc_type() == CERT_VERIFY_PROC_BUILTIN; } + bool SupportsSoftFailRevChecking() const { + return verify_proc_type() == CERT_VERIFY_PROC_NSS || + verify_proc_type() == CERT_VERIFY_PROC_WIN || + verify_proc_type() == CERT_VERIFY_PROC_MAC || + verify_proc_type() == CERT_VERIFY_PROC_BUILTIN; + } + + bool SupportsRevCheckingRequiredLocalAnchors() const { + return verify_proc_type() == CERT_VERIFY_PROC_NSS || + verify_proc_type() == CERT_VERIFY_PROC_WIN || + verify_proc_type() == CERT_VERIFY_PROC_BUILTIN; + } + CertVerifyProc* verify_proc() const { return verify_proc_.get(); } private: @@ -2806,14 +2915,17 @@ // Registers a handler with the test server that responds with the given // Content-Type, HTTP status code, and response body, for GET requests // to |path|. - void RegisterSimpleTestServerHandler(std::string path, + // Returns the full URL to |path| for the current test server. + GURL RegisterSimpleTestServerHandler(std::string path, HttpStatusCode status_code, std::string content_type, std::string content) { + GURL handler_url(GetTestServerAbsoluteUrl(path)); base::AutoLock lock(request_handlers_lock_); request_handlers_.push_back(base::BindRepeating( &SimpleTestServerHandler, std::move(path), status_code, std::move(content_type), std::move(content))); + return handler_url; } // Returns a random URL path (starting with /) that has the given suffix. @@ -2826,15 +2938,14 @@ return test_server_.GetURL(path); } - // Creates a certificate chain for www.example.com, where the leaf certificate - // has an AIA URL pointing to the test server. - void CreateSimpleChainWithAIA( - scoped_refptr<X509Certificate>* out_leaf, - std::string* ca_issuers_path, - bssl::UniquePtr<CRYPTO_BUFFER>* out_intermediate, - scoped_refptr<X509Certificate>* out_root) { + // Creates a simple leaf->intermediate->root chain of CertBuilders with no AIA + // or CrlDistributionPoint extensions, and leaf having a subjectAltName of + // www.example.com. + static void CreateSimpleCertBuilderChain( + std::unique_ptr<CertBuilder>* out_leaf, + std::unique_ptr<CertBuilder>* out_intermediate, + std::unique_ptr<CertBuilder>* out_root) { const char kHostname[] = "www.example.com"; - base::FilePath certs_dir = GetTestNetDataDirectory() .AppendASCII("verify_certificate_chain_unittest") @@ -2844,24 +2955,142 @@ certs_dir, "chain.pem", X509Certificate::FORMAT_AUTO); ASSERT_EQ(3U, orig_certs.size()); - // Build a slightly modified variant of |orig_certs|. - CertBuilder root(orig_certs[2]->cert_buffer(), nullptr); - CertBuilder intermediate(orig_certs[1]->cert_buffer(), &root); - CertBuilder leaf(orig_certs[0]->cert_buffer(), &intermediate); + // Build slightly modified variants of |orig_certs|. + *out_root = + std::make_unique<CertBuilder>(orig_certs[2]->cert_buffer(), nullptr); + *out_intermediate = std::make_unique<CertBuilder>( + orig_certs[1]->cert_buffer(), out_root->get()); + (*out_intermediate)->EraseExtension(CrlDistributionPointsOid()); + (*out_intermediate)->EraseExtension(AuthorityInfoAccessOid()); + *out_leaf = std::make_unique<CertBuilder>(orig_certs[0]->cert_buffer(), + out_intermediate->get()); + (*out_leaf)->SetSubjectAltName(kHostname); + (*out_leaf)->EraseExtension(CrlDistributionPointsOid()); + (*out_leaf)->EraseExtension(AuthorityInfoAccessOid()); + } + + // Creates a certificate chain for www.example.com, where the leaf certificate + // has an AIA URL pointing to the test server. + void CreateSimpleChainWithAIA( + scoped_refptr<X509Certificate>* out_leaf, + std::string* ca_issuers_path, + bssl::UniquePtr<CRYPTO_BUFFER>* out_intermediate, + scoped_refptr<X509Certificate>* out_root) { + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); // Make the leaf certificate have an AIA (CA Issuers) that points to the // embedded test server. This uses a random URL for predictable behavior in // the presence of global caching. *ca_issuers_path = MakeRandomPath(".cer"); GURL ca_issuers_url = GetTestServerAbsoluteUrl(*ca_issuers_path); - leaf.SetCaIssuersUrl(ca_issuers_url); - leaf.SetSubjectAltName(kHostname); + leaf->SetCaIssuersUrl(ca_issuers_url); // The chain being verified is solely the leaf certificate (missing the // intermediate and root). - *out_leaf = leaf.GetX509Certificate(); - *out_root = root.GetX509Certificate(); - *out_intermediate = intermediate.DupCertBuffer(); + *out_leaf = leaf->GetX509Certificate(); + *out_root = root->GetX509Certificate(); + *out_intermediate = intermediate->DupCertBuffer(); + } + + // Creates a CRL issued and signed by |crl_issuer|, marking |revoked_serials| + // as revoked. + // Returns the DER-encoded CRL. + static std::string CreateCrl(CertBuilder* crl_issuer, + const std::vector<uint64_t>& revoked_serials) { + // TBSCertList ::= SEQUENCE { + // version Version OPTIONAL, + // -- if present, MUST be v2 + // signature AlgorithmIdentifier, + // issuer Name, + // thisUpdate Time, + // nextUpdate Time OPTIONAL, + // revokedCertificates SEQUENCE OF SEQUENCE { + // userCertificate CertificateSerialNumber, + // revocationDate Time, + // crlEntryExtensions Extensions OPTIONAL + // -- if present, version MUST be v2 + // } OPTIONAL, + // crlExtensions [0] EXPLICIT Extensions OPTIONAL + // -- if present, version MUST be v2 + // } + bssl::ScopedCBB tbs_cbb; + CBB tbs_cert_list, revoked_serials_cbb; + if (!CBB_init(tbs_cbb.get(), 10) || + !CBB_add_asn1(tbs_cbb.get(), &tbs_cert_list, CBS_ASN1_SEQUENCE) || + !CBB_add_asn1_uint64(&tbs_cert_list, 1 /* V2 */) || + !CBBAddBytes(&tbs_cert_list, Sha256WithRSAEncryption()) || + !CBBAddBytes(&tbs_cert_list, crl_issuer->GetSubject()) || + !CBBAddTime(&tbs_cert_list, + base::Time::Now() - base::TimeDelta::FromDays(1)) || + !CBBAddTime(&tbs_cert_list, + base::Time::Now() + base::TimeDelta::FromDays(6))) { + ADD_FAILURE(); + return std::string(); + } + if (!revoked_serials.empty()) { + if (!CBB_add_asn1(&tbs_cert_list, &revoked_serials_cbb, + CBS_ASN1_SEQUENCE)) { + ADD_FAILURE(); + return std::string(); + } + for (const int64_t revoked_serial : revoked_serials) { + CBB revoked_serial_cbb; + if (!CBB_add_asn1(&revoked_serials_cbb, &revoked_serial_cbb, + CBS_ASN1_SEQUENCE) || + !CBB_add_asn1_uint64(&revoked_serial_cbb, revoked_serial) || + !CBBAddTime(&revoked_serial_cbb, + base::Time::Now() - base::TimeDelta::FromDays(1)) || + !CBB_flush(&revoked_serials_cbb)) { + ADD_FAILURE(); + return std::string(); + } + } + } + + std::string tbs_tlv = FinishCBB(tbs_cbb.get()); + + // CertificateList ::= SEQUENCE { + // tbsCertList TBSCertList, + // signatureAlgorithm AlgorithmIdentifier, + // signatureValue BIT STRING } + bssl::ScopedCBB crl_cbb; + CBB cert_list, signature; + bssl::ScopedEVP_MD_CTX ctx; + uint8_t* sig_out; + size_t sig_len; + if (!CBB_init(crl_cbb.get(), 10) || + !CBB_add_asn1(crl_cbb.get(), &cert_list, CBS_ASN1_SEQUENCE) || + !CBBAddBytes(&cert_list, tbs_tlv) || + !CBBAddBytes(&cert_list, Sha256WithRSAEncryption()) || + !CBB_add_asn1(&cert_list, &signature, CBS_ASN1_BITSTRING) || + !CBB_add_u8(&signature, 0 /* no unused bits */) || + !EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr, + crl_issuer->GetKey()) || + !EVP_DigestSign(ctx.get(), nullptr, &sig_len, + reinterpret_cast<const uint8_t*>(tbs_tlv.data()), + tbs_tlv.size()) || + !CBB_reserve(&signature, &sig_out, sig_len) || + !EVP_DigestSign(ctx.get(), sig_out, &sig_len, + reinterpret_cast<const uint8_t*>(tbs_tlv.data()), + tbs_tlv.size()) || + !CBB_did_write(&signature, sig_len)) { + ADD_FAILURE(); + return std::string(); + } + return FinishCBB(crl_cbb.get()); + } + + // Creates a CRL issued and signed by |crl_issuer|, marking |revoked_serials| + // as revoked, and registers it to be served by the test server. + // Returns the full URL to retrieve the CRL from the test server. + GURL CreateAndServeCrl(CertBuilder* crl_issuer, + const std::vector<uint64_t>& revoked_serials) { + std::string crl = CreateCrl(crl_issuer, revoked_serials); + std::string crl_path = MakeRandomPath(".crl"); + return RegisterSimpleTestServerHandler(crl_path, HTTP_OK, + "application/pkix-crl", crl); } private: @@ -3244,6 +3473,643 @@ } } +TEST_P(CertVerifyProcInternalWithNetFetchingTest, RevocationHardFailNoCrls) { + if (!SupportsRevCheckingRequiredLocalAnchors()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS"; + return; + } + + // Create certs which have no AIA or CRL distribution points. + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with hard-fail revocation checking for local anchors. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + // "Hard fail" handling varies for certificates that have no revocation + // mechanisms at all. Some implementations consider it okay, some consider it + // a failure. + if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN || + verify_proc_type() == CERT_VERIFY_PROC_WIN) { + EXPECT_THAT(error, IsOk()); + } else { + EXPECT_THAT(error, IsError(ERR_CERT_REVOKED)); + } + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +// CRL hard fail test where both leaf and intermediate are covered by valid +// CRLs which have empty (non-present) revokedCertificates list. Verification +// should succeed. +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationHardFailCrlGoodNoRevokedCertificates) { + if (!SupportsRevCheckingRequiredLocalAnchors()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS"; + return; + } + + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Serve a root-issued CRL which does not revoke intermediate. + intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {})); + + // Serve an intermediate-issued CRL which does not revoke leaf. + leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(intermediate.get(), {})); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with hard-fail revocation checking for local anchors. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + // Should pass, leaf and intermediate were covered by CRLs and were not + // revoked. + EXPECT_THAT(error, IsOk()); + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +// CRL hard fail test where both leaf and intermediate are covered by valid +// CRLs which have revokedCertificates lists that revoke other irrelevant +// serial numbers. Verification should succeed. +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationHardFailCrlGoodIrrelevantSerialsRevoked) { + if (!SupportsRevCheckingRequiredLocalAnchors()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS"; + return; + } + + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Root-issued CRL revokes leaf's serial number. This is irrelevant. + intermediate->SetCrlDistributionPointUrl( + CreateAndServeCrl(root.get(), {leaf->GetSerialNumber()})); + + // Intermediate-issued CRL revokes intermediates's serial number. This is + // irrelevant. + leaf->SetCrlDistributionPointUrl( + CreateAndServeCrl(intermediate.get(), {intermediate->GetSerialNumber()})); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with hard-fail revocation checking for local anchors. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + // Should pass, leaf and intermediate were covered by CRLs and were not + // revoked. + EXPECT_THAT(error, IsOk()); + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationHardFailLeafRevokedByCrl) { + if (!SupportsRevCheckingRequiredLocalAnchors()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS"; + return; + } + + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Root-issued CRL which does not revoke intermediate. + intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {})); + + // Leaf is revoked by intermediate issued CRL. + leaf->SetCrlDistributionPointUrl( + CreateAndServeCrl(intermediate.get(), {leaf->GetSerialNumber()})); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with hard-fail revocation checking for local anchors. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + // Should fail, leaf is revoked. + if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) { + // TODO(mattm): CertVerifyProcBuiltin CRL handling. + EXPECT_THAT(error, IsOk()); + } else { + EXPECT_THAT(error, IsError(ERR_CERT_REVOKED)); + } + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationHardFailIntermediateRevokedByCrl) { + if (!SupportsRevCheckingRequiredLocalAnchors()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS"; + return; + } + + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Intermediate is revoked by root issued CRL. + intermediate->SetCrlDistributionPointUrl( + CreateAndServeCrl(root.get(), {intermediate->GetSerialNumber()})); + + // Intermediate-issued CRL which does not revoke leaf. + leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(intermediate.get(), {})); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with hard-fail revocation checking for local anchors. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + // Should fail, intermediate is revoked. + if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) { + // TODO(mattm): CertVerifyProcBuiltin CRL handling. + EXPECT_THAT(error, IsOk()); + } else { + EXPECT_THAT(error, IsError(ERR_CERT_REVOKED)); + } + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +// CRL hard fail test where the intermediate certificate has a good CRL, but +// the leaf's distribution point returns an http error. Verification should +// fail. +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationHardFailLeafCrlDpHttpError) { + if (!SupportsRevCheckingRequiredLocalAnchors()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS"; + return; + } + + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Serve a root-issued CRL which does not revoke intermediate. + intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {})); + + // Serve a 404 for the intermediate-issued CRL distribution point url. + leaf->SetCrlDistributionPointUrl(RegisterSimpleTestServerHandler( + MakeRandomPath(".crl"), HTTP_NOT_FOUND, "text/plain", "Not Found")); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with hard-fail revocation checking for local anchors. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + // Should fail since no revocation information was available for the leaf. + if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) { + // TODO(mattm): CertVerifyProcBuiltin CRL handling. + EXPECT_THAT(error, IsOk()); + } else { + EXPECT_THAT(error, IsError(ERR_CERT_REVOKED)); + } + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +// CRL hard fail test where the leaf certificate has a good CRL, but +// the intermediate's distribution point returns an http error. Verification +// should fail. +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationHardFailIntermediateCrlDpHttpError) { + if (!SupportsRevCheckingRequiredLocalAnchors()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS"; + return; + } + + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Serve a 404 for the root-issued CRL distribution point url. + intermediate->SetCrlDistributionPointUrl(RegisterSimpleTestServerHandler( + MakeRandomPath(".crl"), HTTP_NOT_FOUND, "text/plain", "Not Found")); + + // Serve an intermediate-issued CRL which does not revoke leaf. + leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(intermediate.get(), {})); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with hard-fail revocation checking for local anchors. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + // Should fail since no revocation information was available for the + // intermediate. + if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) { + // TODO(mattm): CertVerifyProcBuiltin CRL handling. + EXPECT_THAT(error, IsOk()); + } else { + EXPECT_THAT(error, IsError(ERR_CERT_REVOKED)); + } + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +TEST_P(CertVerifyProcInternalWithNetFetchingTest, RevocationSoftFailNoCrls) { + if (!SupportsSoftFailRevChecking()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_ENABLED"; + return; + } + + // Create certs which have no AIA or CRL distribution points. + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with soft-fail revocation checking. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + if (verify_proc_type() == CERT_VERIFY_PROC_MAC && IsMacAtLeastOS10_12()) { + // CRL handling seems broken on macOS >= 10.12. + // TODO(mattm): followup on this. + EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION)); + } else { + // Soft-fail revocation checking should succeed when no revocation mechanism + // is available. + EXPECT_THAT(error, IsOk()); + } + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +// CRL soft fail test where both leaf and intermediate are covered by valid +// CRLs which have empty (non-present) revokedCertificates list. Verification +// should succeed. +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationSoftFailCrlGoodNoRevokedCertificates) { + if (!SupportsSoftFailRevChecking()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_ENABLED"; + return; + } + + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Serve a root-issued CRL which does not revoke intermediate. + intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {})); + + // Serve an intermediate-issued CRL which does not revoke leaf. + leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(intermediate.get(), {})); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with soft-fail revocation checking. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + if (verify_proc_type() == CERT_VERIFY_PROC_MAC && IsMacAtLeastOS10_12()) { + // CRL handling seems broken on macOS >= 10.12. + // TODO(mattm): followup on this. + EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION)); + } else { + // Should pass, leaf and intermediate were covered by CRLs and were not + // revoked. + EXPECT_THAT(error, IsOk()); + } + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +// CRL soft fail test where both leaf and intermediate are covered by valid +// CRLs which have revokedCertificates lists that revoke other irrelevant +// serial numbers. Verification should succeed. +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationSoftFailCrlGoodIrrelevantSerialsRevoked) { + if (!SupportsSoftFailRevChecking()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_ENABLED"; + return; + } + + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Root-issued CRL revokes leaf's serial number. This is irrelevant. + intermediate->SetCrlDistributionPointUrl( + CreateAndServeCrl(root.get(), {leaf->GetSerialNumber()})); + + // Intermediate-issued CRL revokes intermediates's serial number. This is + // irrelevant. + leaf->SetCrlDistributionPointUrl( + CreateAndServeCrl(intermediate.get(), {intermediate->GetSerialNumber()})); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with soft-fail revocation checking. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + if (verify_proc_type() == CERT_VERIFY_PROC_MAC && IsMacAtLeastOS10_12()) { + // CRL handling seems broken on macOS >= 10.12. + // TODO(mattm): followup on this. + EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION)); + } else { + // Should pass, leaf and intermediate were covered by CRLs and were not + // revoked. + EXPECT_THAT(error, IsOk()); + } + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationSoftFailLeafRevokedByCrl) { + if (!SupportsSoftFailRevChecking()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_ENABLED"; + return; + } + + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Root-issued CRL which does not revoke intermediate. + intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {})); + + // Leaf is revoked by intermediate issued CRL. + leaf->SetCrlDistributionPointUrl( + CreateAndServeCrl(intermediate.get(), {leaf->GetSerialNumber()})); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with soft-fail revocation checking. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) { + // TODO(mattm): CertVerifyProcBuiltin CRL handling. + EXPECT_THAT(error, IsOk()); + } else if (verify_proc_type() == CERT_VERIFY_PROC_MAC && + IsMacAtLeastOS10_12()) { + // CRL handling seems broken on macOS >= 10.12. + // TODO(mattm): followup on this. + EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION)); + } else { + // Should fail, leaf is revoked. + EXPECT_THAT(error, IsError(ERR_CERT_REVOKED)); + } + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationSoftFailIntermediateRevokedByCrl) { + if (!SupportsSoftFailRevChecking()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_ENABLED"; + return; + } + + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Intermediate is revoked by root issued CRL. + intermediate->SetCrlDistributionPointUrl( + CreateAndServeCrl(root.get(), {intermediate->GetSerialNumber()})); + + // Intermediate-issued CRL which does not revoke leaf. + leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(intermediate.get(), {})); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with soft-fail revocation checking. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + if (verify_proc_type() == CERT_VERIFY_PROC_BUILTIN) { + // TODO(mattm): CertVerifyProcBuiltin CRL handling. + EXPECT_THAT(error, IsOk()); + } else if (verify_proc_type() == CERT_VERIFY_PROC_MAC && + IsMacAtLeastOS10_12()) { + // CRL handling seems broken on macOS >= 10.12. + // TODO(mattm): followup on this. + EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION)); + } else { + // Should fail, intermediate is revoked. + EXPECT_THAT(error, IsError(ERR_CERT_REVOKED)); + } + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +// CRL soft fail test where the intermediate certificate has a good CRL, but +// the leaf's distribution point returns an http error. Verification should +// succeed. +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationSoftFailLeafCrlDpHttpError) { + if (!SupportsSoftFailRevChecking()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_ENABLED"; + return; + } + + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Serve a root-issued CRL which does not revoke intermediate. + intermediate->SetCrlDistributionPointUrl(CreateAndServeCrl(root.get(), {})); + + // Serve a 404 for the intermediate-issued CRL distribution point url. + leaf->SetCrlDistributionPointUrl(RegisterSimpleTestServerHandler( + MakeRandomPath(".crl"), HTTP_NOT_FOUND, "text/plain", "Not Found")); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with soft-fail revocation checking. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + if (verify_proc_type() == CERT_VERIFY_PROC_WIN) { + // Win gives ERR_CERT_UNABLE_TO_CHECK_REVOCATION if the revocation checking + // network requests failed. TODO(mattm): should we ignore that? + EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION)); + } else if (verify_proc_type() == CERT_VERIFY_PROC_MAC && + IsMacAtLeastOS10_12()) { + // CRL handling seems broken on macOS >= 10.12. + // TODO(mattm): followup on this. + EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION)); + } else { + // Should succeed due to soft-fail revocation checking. + EXPECT_THAT(error, IsOk()); + } + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + +// CRL soft fail test where the leaf certificate has a good CRL, but +// the intermediate's distribution point returns an http error. Verification +// should succeed. +TEST_P(CertVerifyProcInternalWithNetFetchingTest, + RevocationSoftFailIntermediateCrlDpHttpError) { + if (!SupportsSoftFailRevChecking()) { + LOG(INFO) << "Skipping test as verifier doesn't support " + "VERIFY_REV_CHECKING_ENABLED"; + return; + } + + const char kHostname[] = "www.example.com"; + std::unique_ptr<CertBuilder> leaf, intermediate, root; + CreateSimpleCertBuilderChain(&leaf, &intermediate, &root); + ASSERT_TRUE(leaf && intermediate && root); + + // Serve a 404 for the root-issued CRL distribution point url. + intermediate->SetCrlDistributionPointUrl(RegisterSimpleTestServerHandler( + MakeRandomPath(".crl"), HTTP_NOT_FOUND, "text/plain", "Not Found")); + + // Serve an intermediate-issued CRL which does not revoke leaf. + leaf->SetCrlDistributionPointUrl(CreateAndServeCrl(intermediate.get(), {})); + + // Trust the root and build a chain to verify that includes the intermediate. + ScopedTestRoot scoped_root(root->GetX509Certificate().get()); + scoped_refptr<X509Certificate> chain = CreateX509CertificateWithIntermediate( + leaf->DupCertBuffer(), intermediate->DupCertBuffer()); + ASSERT_TRUE(chain.get()); + + // Verify with soft-fail revocation checking. + const int flags = CertVerifyProc::VERIFY_REV_CHECKING_ENABLED; + CertVerifyResult verify_result; + int error = + Verify(chain.get(), kHostname, flags, CRLSet::BuiltinCRLSet().get(), + CertificateList(), &verify_result); + + if (verify_proc_type() == CERT_VERIFY_PROC_WIN) { + // Win gives ERR_CERT_UNABLE_TO_CHECK_REVOCATION if the revocation checking + // network requests failed. TODO(mattm): should we ignore that? + EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION)); + } else if (verify_proc_type() == CERT_VERIFY_PROC_MAC && + IsMacAtLeastOS10_12()) { + // CRL handling seems broken on macOS >= 10.12. + // TODO(mattm): followup on this. + EXPECT_THAT(error, IsError(ERR_CERT_UNABLE_TO_CHECK_REVOCATION)); + } else { + // Should succeed due to soft-fail revocation checking. + EXPECT_THAT(error, IsOk()); + } + EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED); +} + TEST(CertVerifyProcTest, RejectsMD2) { scoped_refptr<X509Certificate> cert( ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
diff --git a/net/cert/crl_set_fuzzer.cc b/net/cert/crl_set_fuzzer.cc new file mode 100644 index 0000000..97d029c --- /dev/null +++ b/net/cert/crl_set_fuzzer.cc
@@ -0,0 +1,17 @@ +// Copyright 2019 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 <stddef.h> +#include <stdint.h> + +#include "base/test/fuzzed_data_provider.h" +#include "net/cert/crl_set.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + base::FuzzedDataProvider data_provider(data, size); + const std::string str = data_provider.ConsumeRandomLengthString(size); + scoped_refptr<net::CRLSet> out_crl_set; + net::CRLSet::Parse(str, &out_crl_set); + return 0; +}
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_intermediate_serial.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_intermediate_serial.raw new file mode 100644 index 0000000..370d488 --- /dev/null +++ b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_intermediate_serial.raw Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_leaf_spki.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_leaf_spki.raw new file mode 100644 index 0000000..acc4c15 --- /dev/null +++ b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_leaf_spki.raw Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_leaf_subject_no_spki.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_leaf_subject_no_spki.raw new file mode 100644 index 0000000..3a22aa7d --- /dev/null +++ b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_leaf_subject_no_spki.raw Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_serial.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_serial.raw new file mode 100644 index 0000000..31f6b462 --- /dev/null +++ b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_serial.raw Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_subject.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_subject.raw new file mode 100644 index 0000000..95404126 --- /dev/null +++ b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_subject.raw Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_subject_no_spki.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_subject_no_spki.raw new file mode 100644 index 0000000..bc10681c --- /dev/null +++ b/net/data/fuzzer_data/net_crl_set_fuzzer/crlset_by_root_subject_no_spki.raw Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-C.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-C.raw new file mode 100644 index 0000000..c8ff83b --- /dev/null +++ b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-C.raw Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-CD-and-FE.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-CD-and-FE.raw new file mode 100644 index 0000000..4b04356 --- /dev/null +++ b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-CD-and-FE.raw Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-D-and-E.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-D-and-E.raw new file mode 100644 index 0000000..eacdb4aaf --- /dev/null +++ b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-D-and-E.raw Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-E.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-E.raw new file mode 100644 index 0000000..02051b8f --- /dev/null +++ b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-E.raw Binary files differ
diff --git a/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-unrelated.raw b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-unrelated.raw new file mode 100644 index 0000000..83fd62c --- /dev/null +++ b/net/data/fuzzer_data/net_crl_set_fuzzer/multi-root-crlset-unrelated.raw Binary files differ
diff --git a/net/data/ocsp_unittest/make_ocsp.py b/net/data/ocsp_unittest/make_ocsp.py index abbc5a47..4c5032e 100755 --- a/net/data/ocsp_unittest/make_ocsp.py +++ b/net/data/ocsp_unittest/make_ocsp.py
@@ -289,8 +289,6 @@ ocsp_request_der = CreateOCSPRequestDer(ca_cert_pem, cert_pem) - d64 = base64.b64encode(encoder.encode(data)) - wd64 = '\n'.join(d64[pos:pos + 64] for pos in xrange(0, len(d64), 64)) out = ('%s\n%s\n%s\n\n%s\n%s') % ( description, MakePemBlock(encoder.encode(data), "OCSP RESPONSE"),
diff --git a/net/data/ssl/certificates/README b/net/data/ssl/certificates/README index 32c7d287..9f9714f6 100644 --- a/net/data/ssl/certificates/README +++ b/net/data/ssl/certificates/README
@@ -95,10 +95,6 @@ - unittest.key.bin : private key stored unencrypted. -- unittest.originbound.der: A test origin-bound certificate for - https://www.google.com:443. -- unittest.originbound.key.der: matching PrivateKeyInfo. - - multivalue_rdn.pem : A regression test for http://crbug.com/101009. A certificate with all of the AttributeTypeAndValues stored within a single RelativeDistinguishedName, rather than one AVA per RDN as normally seen.
diff --git a/net/data/ssl/certificates/unittest.originbound.der b/net/data/ssl/certificates/unittest.originbound.der deleted file mode 100644 index b37f593..0000000 --- a/net/data/ssl/certificates/unittest.originbound.der +++ /dev/null Binary files differ
diff --git a/net/data/ssl/certificates/unittest.originbound.key.der b/net/data/ssl/certificates/unittest.originbound.key.der deleted file mode 100644 index 1ecdd1eb..0000000 --- a/net/data/ssl/certificates/unittest.originbound.key.der +++ /dev/null Binary files differ
diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc index c9b32bbb..e8eab5d2 100644 --- a/net/disk_cache/simple/simple_backend_impl.cc +++ b/net/disk_cache/simple/simple_backend_impl.cc
@@ -23,12 +23,12 @@ #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" -#include "base/single_thread_task_runner.h" +#include "base/sequenced_task_runner.h" #include "base/system/sys_info.h" #include "base/task/post_task.h" #include "base/task/thread_pool/thread_pool.h" #include "base/task_runner_util.h" -#include "base/threading/thread_task_runner_handle.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "base/time/time.h" #include "base/trace_event/memory_usage_estimator.h" #include "base/trace_event/process_memory_dump.h" @@ -286,7 +286,7 @@ base::MakeRefCounted<net::PrioritizedTaskRunner>(worker_pool); index_ = std::make_unique<SimpleIndex>( - base::ThreadTaskRunnerHandle::Get(), cleanup_tracker_.get(), this, + base::SequencedTaskRunnerHandle::Get(), cleanup_tracker_.get(), this, GetCacheType(), std::make_unique<SimpleIndexFile>(cache_runner_, worker_pool.get(), GetCacheType(), path_));
diff --git a/net/disk_cache/simple/simple_backend_impl.h b/net/disk_cache/simple/simple_backend_impl.h index 4237f85c..ccf994340 100644 --- a/net/disk_cache/simple/simple_backend_impl.h +++ b/net/disk_cache/simple/simple_backend_impl.h
@@ -41,15 +41,17 @@ // SimpleBackendImpl is a new cache backend that stores entries in individual // files. -// See http://www.chromium.org/developers/design-documents/network-stack/disk-cache/very-simple-backend +// See +// http://www.chromium.org/developers/design-documents/network-stack/disk-cache/very-simple-backend // // The SimpleBackendImpl provides safe iteration; mutating entries during // iteration cannot cause a crash. It is undefined whether entries created or // destroyed during the iteration will be included in any pre-existing // iterations. // -// The non-static functions below must be called on the IO thread unless -// otherwise stated. +// The non-static functions below must be called on the source creation sequence +// unless otherwise stated. Historically the source creation sequence has been +// the IO thread, but the simple backend may now be used from other sequences. class BackendCleanupTracker; class SimpleEntryImpl; @@ -204,8 +206,8 @@ Int64CompletionOnceCallback callback, int result); - // Try to create the directory if it doesn't exist. This must run on the IO - // thread. + // Try to create the directory if it doesn't exist. This must run on the + // source creation sequence. static DiskStatResult InitCacheStructureOnDisk(const base::FilePath& path, uint64_t suggested_max_size, net::CacheType cache_type);
diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc index 4828fcf..2a4754f 100644 --- a/net/disk_cache/simple/simple_entry_impl.cc +++ b/net/disk_cache/simple/simple_entry_impl.cc
@@ -15,11 +15,10 @@ #include "base/callback.h" #include "base/location.h" #include "base/logging.h" -#include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/task_runner.h" #include "base/task_runner_util.h" -#include "base/threading/thread_task_runner_handle.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "base/time/time.h" #include "base/trace_event/memory_usage_estimator.h" #include "net/base/io_buffer.h" @@ -125,7 +124,7 @@ net::CompletionOnceCallback callback, int rv) { if (!sync_possible && !callback.is_null()) { - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), rv)); return net::ERR_IO_PENDING; } else { @@ -383,7 +382,7 @@ } void SimpleEntryImpl::Close() { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_LT(0, open_count_); net_log_.AddEvent(net::NetLogEventType::SIMPLE_CACHE_ENTRY_CLOSE_CALL); @@ -401,23 +400,23 @@ } std::string SimpleEntryImpl::GetKey() const { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return key_; } Time SimpleEntryImpl::GetLastUsed() const { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(cache_type_ != net::APP_CACHE); return last_used_; } Time SimpleEntryImpl::GetLastModified() const { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return last_modified_; } int32_t SimpleEntryImpl::GetDataSize(int stream_index) const { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_LE(0, data_size_[stream_index]); return data_size_[stream_index]; } @@ -427,7 +426,7 @@ net::IOBuffer* buf, int buf_len, CompletionOnceCallback callback) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (net_log_.IsCapturing()) { net_log_.AddEvent(net::NetLogEventType::SIMPLE_CACHE_ENTRY_READ_CALL, @@ -471,7 +470,7 @@ int buf_len, CompletionOnceCallback callback, bool truncate) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (net_log_.IsCapturing()) { net_log_.AddEvent(net::NetLogEventType::SIMPLE_CACHE_ENTRY_WRITE_CALL, @@ -551,7 +550,7 @@ net::IOBuffer* buf, int buf_len, CompletionOnceCallback callback) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (net_log_.IsCapturing()) { net_log_.AddEvent(net::NetLogEventType::SIMPLE_CACHE_ENTRY_READ_SPARSE_CALL, @@ -577,7 +576,7 @@ net::IOBuffer* buf, int buf_len, CompletionOnceCallback callback) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (net_log_.IsCapturing()) { net_log_.AddEvent( @@ -604,7 +603,7 @@ int len, int64_t* start, CompletionOnceCallback callback) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (offset < 0 || len < 0) return net::ERR_INVALID_ARGUMENT; @@ -615,20 +614,20 @@ } bool SimpleEntryImpl::CouldBeSparse() const { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // TODO(morlovich): Actually check. return true; } void SimpleEntryImpl::CancelSparseIO() { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // The Simple Cache does not return distinct objects for the same non-doomed // entry, so there's no need to coordinate which object is performing sparse // I/O. Therefore, CancelSparseIO and ReadyForSparseIO succeed instantly. } net::Error SimpleEntryImpl::ReadyForSparseIO(CompletionOnceCallback callback) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // The simple Cache does not return distinct objects for the same non-doomed // entry, so there's no need to coordinate which object is performing sparse // I/O. Therefore, CancelSparseIO and ReadyForSparseIO succeed instantly. @@ -654,7 +653,7 @@ } SimpleEntryImpl::~SimpleEntryImpl() { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_EQ(0U, pending_operations_.size()); // STATE_IO_PENDING is possible here in one corner case: the entry had @@ -675,7 +674,7 @@ return; // Note that the callback is posted rather than directly invoked to avoid // reentrancy issues. - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&InvokeCallbackIfBackendIsAlive, backend_, std::move(callback), result)); } @@ -724,7 +723,7 @@ // potentially call ReturnEntryToCaller and then roll it back rather than // delay ReturnEntryToCaller: it protects |this| till any potential ownership // share transfer is sorted out. - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&InvokeCallbackIfBackendIsAliveOrCloseEntry, backend_, base::Unretained(this), std::move(callback))); @@ -740,7 +739,7 @@ } void SimpleEntryImpl::RunNextOperationIfNeeded() { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!pending_operations_.empty() && state_ != STATE_IO_PENDING) { SimpleEntryOperation operation = std::move(pending_operations_.front()); pending_operations_.pop(); @@ -953,7 +952,7 @@ } void SimpleEntryImpl::CloseInternal() { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (open_count_ != 0) { // Entry got resurrected in between Close and CloseInternal, nothing to do @@ -1017,7 +1016,7 @@ net::IOBuffer* buf, int buf_len, net::CompletionOnceCallback callback) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ScopedOperationRunner operation_runner(this); if (net_log_.IsCapturing()) { @@ -1111,7 +1110,7 @@ int buf_len, net::CompletionOnceCallback callback, bool truncate) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ScopedOperationRunner operation_runner(this); if (net_log_.IsCapturing()) { @@ -1127,7 +1126,7 @@ CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED)); } if (!callback.is_null()) { - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), net::ERR_FAILED)); } // |this| may be destroyed after return here. @@ -1140,7 +1139,7 @@ if (stream_index == 0) { int ret_value = SetStream0Data(buf, offset, buf_len, truncate); if (!callback.is_null()) { - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), ret_value)); } return; @@ -1153,7 +1152,7 @@ RecordWriteResult(cache_type_, SIMPLE_ENTRY_WRITE_RESULT_FAST_EMPTY_RETURN); if (!callback.is_null()) { - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), 0)); } return; @@ -1229,7 +1228,7 @@ net::IOBuffer* buf, int buf_len, net::CompletionOnceCallback callback) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ScopedOperationRunner operation_runner(this); if (net_log_.IsCapturing()) { @@ -1245,7 +1244,7 @@ CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED)); } if (!callback.is_null()) { - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), net::ERR_FAILED)); } // |this| may be destroyed after return here. @@ -1274,7 +1273,7 @@ net::IOBuffer* buf, int buf_len, net::CompletionOnceCallback callback) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ScopedOperationRunner operation_runner(this); if (net_log_.IsCapturing()) { @@ -1290,7 +1289,7 @@ CreateNetLogReadWriteCompleteCallback(net::ERR_FAILED)); } if (!callback.is_null()) { - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), net::ERR_FAILED)); } // |this| may be destroyed after return here. @@ -1330,12 +1329,12 @@ int len, int64_t* out_start, net::CompletionOnceCallback callback) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ScopedOperationRunner operation_runner(this); if (state_ == STATE_FAILURE || state_ == STATE_UNINITIALIZED) { if (!callback.is_null()) { - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), net::ERR_FAILED)); } // |this| may be destroyed after return here. @@ -1424,7 +1423,7 @@ Entry** out_entry, bool* out_opened, net::NetLogEventType end_event_type) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_EQ(state_, STATE_IO_PENDING); DCHECK(in_results); ScopedOperationRunner operation_runner(this); @@ -1528,7 +1527,7 @@ net::CompletionOnceCallback completion_callback, const SimpleEntryStat& entry_stat, int result) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(synchronous_entry_); DCHECK_EQ(STATE_IO_PENDING, state_); if (result < 0) { @@ -1540,7 +1539,7 @@ } if (!completion_callback.is_null()) { - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(completion_callback), result)); } RunNextOperationIfNeeded(); @@ -1552,7 +1551,7 @@ net::CompletionOnceCallback completion_callback, std::unique_ptr<SimpleEntryStat> entry_stat, std::unique_ptr<SimpleSynchronousEntry::ReadResult> read_result) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(synchronous_entry_); DCHECK_EQ(STATE_IO_PENDING, state_); DCHECK(read_result); @@ -1623,7 +1622,7 @@ net::CompletionOnceCallback completion_callback, std::unique_ptr<base::Time> last_used, std::unique_ptr<int> result) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(synchronous_entry_); DCHECK(result); @@ -1641,7 +1640,7 @@ net::CompletionOnceCallback completion_callback, std::unique_ptr<SimpleEntryStat> entry_stat, std::unique_ptr<int> result) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(synchronous_entry_); DCHECK(result); @@ -1656,7 +1655,7 @@ void SimpleEntryImpl::GetAvailableRangeOperationComplete( net::CompletionOnceCallback completion_callback, std::unique_ptr<int> result) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(synchronous_entry_); DCHECK(result); @@ -1681,7 +1680,7 @@ void SimpleEntryImpl::RecordReadResultConsideringChecksum( const std::unique_ptr<SimpleSynchronousEntry::ReadResult>& read_result) const { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(synchronous_entry_); DCHECK_EQ(STATE_IO_PENDING, state_); @@ -1716,7 +1715,7 @@ void SimpleEntryImpl::UpdateDataFromEntryStat( const SimpleEntryStat& entry_stat) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(synchronous_entry_); DCHECK_EQ(STATE_READY, state_); @@ -1787,7 +1786,7 @@ base::Time modification_time = base::Time::Now(); // Reset checksum; SimpleSynchronousEntry::Close will compute it for us, - // and do it off the I/O thread. + // and do it off the source creation sequence. crc32s_end_offset_[0] = 0; UpdateDataFromEntryStat(
diff --git a/net/disk_cache/simple/simple_entry_impl.h b/net/disk_cache/simple/simple_entry_impl.h index dbf6098..cae5d5d 100644 --- a/net/disk_cache/simple/simple_entry_impl.h +++ b/net/disk_cache/simple/simple_entry_impl.h
@@ -13,7 +13,7 @@ #include "base/containers/queue.h" #include "base/files/file_path.h" #include "base/memory/ref_counted.h" -#include "base/threading/thread_checker.h" +#include "base/sequence_checker.h" #include "net/base/cache_type.h" #include "net/base/net_export.h" #include "net/base/request_priority.h" @@ -44,9 +44,9 @@ class SimpleSynchronousEntry; struct SimpleEntryCreationResults; -// SimpleEntryImpl is the IO thread interface to an entry in the very simple -// disk cache. It proxies for the SimpleSynchronousEntry, which performs IO -// on the worker thread. +// SimpleEntryImpl is the source task_runner interface to an entry in the very +// simple disk cache. It proxies for the SimpleSynchronousEntry, which performs +// IO on the worker thread. class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, public base::RefCounted<SimpleEntryImpl> { friend class base::RefCounted<SimpleEntryImpl>; @@ -307,7 +307,7 @@ // Called after an asynchronous write completes. // |buf| parameter brings back a reference to net::IOBuffer to the original - // thread, so that we can reduce cross thread malloc/free pair. + // sequence, so that we can reduce cross thread malloc/free pair. // See http://crbug.com/708644 for details. void WriteOperationComplete( int stream_index, @@ -367,9 +367,10 @@ std::unique_ptr<ActiveEntryProxy> active_entry_proxy_; - // All nonstatic SimpleEntryImpl methods should always be called on the IO - // thread, in all cases. |io_thread_checker_| documents and enforces this. - base::ThreadChecker io_thread_checker_; + // All nonstatic SimpleEntryImpl methods should always be called on the + // source creation sequence, in all cases. |sequence_checker_| documents and + // enforces this. + SEQUENCE_CHECKER(sequence_checker_); const base::WeakPtr<SimpleBackendImpl> backend_; SimpleFileTracker* const file_tracker_;
diff --git a/net/disk_cache/simple/simple_index.cc b/net/disk_cache/simple/simple_index.cc index cd12bb20..a4ee6f7 100644 --- a/net/disk_cache/simple/simple_index.cc +++ b/net/disk_cache/simple/simple_index.cc
@@ -177,7 +177,7 @@ } SimpleIndex::SimpleIndex( - const scoped_refptr<base::SingleThreadTaskRunner>& io_thread, + const scoped_refptr<base::SequencedTaskRunner>& task_runner, scoped_refptr<BackendCleanupTracker> cleanup_tracker, SimpleIndexDelegate* delegate, net::CacheType cache_type, @@ -186,7 +186,7 @@ delegate_(delegate), cache_type_(cache_type), index_file_(std::move(index_file)), - io_thread_(io_thread), + task_runner_(task_runner), // Creating the callback once so it is reused every time // write_to_disk_timer_.Start() is called. write_to_disk_cb_(base::Bind(&SimpleIndex::WriteToDisk, @@ -194,7 +194,7 @@ INDEX_WRITE_REASON_IDLE)) {} SimpleIndex::~SimpleIndex() { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // Fail all callbacks waiting for the index to come up. for (auto it = to_run_when_initialized_.begin(), @@ -205,7 +205,7 @@ } void SimpleIndex::Initialize(base::Time cache_mtime) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); #if defined(OS_ANDROID) if (app_status_listener_) { @@ -238,9 +238,9 @@ } net::Error SimpleIndex::ExecuteWhenReady(net::CompletionOnceCallback task) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (initialized_) - io_thread_->PostTask(FROM_HERE, base::BindOnce(std::move(task), net::OK)); + task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(task), net::OK)); else to_run_when_initialized_.push_back(std::move(task)); return net::ERR_IO_PENDING; @@ -316,7 +316,7 @@ } base::Time SimpleIndex::GetLastUsedTime(uint64_t entry_hash) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_NE(cache_type_, net::APP_CACHE); auto it = entries_set_.find(entry_hash); if (it == entries_set_.end()) @@ -336,7 +336,7 @@ } void SimpleIndex::Insert(uint64_t entry_hash) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // Upon insert we don't know yet the size of the entry. // It will be updated later when the SimpleEntryImpl finishes opening or // creating the new entry, and then UpdateEntrySize will be called. @@ -355,7 +355,7 @@ } void SimpleIndex::Remove(uint64_t entry_hash) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); bool need_write = false; auto it = entries_set_.find(entry_hash); if (it != entries_set_.end()) { @@ -372,13 +372,13 @@ } bool SimpleIndex::Has(uint64_t hash) const { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // If not initialized, always return true, forcing it to go to the disk. return !initialized_ || entries_set_.count(hash) > 0; } uint8_t SimpleIndex::GetEntryInMemoryData(uint64_t entry_hash) const { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); auto it = entries_set_.find(entry_hash); if (it == entries_set_.end()) return 0; @@ -386,7 +386,7 @@ } void SimpleIndex::SetEntryInMemoryData(uint64_t entry_hash, uint8_t value) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); auto it = entries_set_.find(entry_hash); if (it == entries_set_.end()) return; @@ -394,7 +394,7 @@ } bool SimpleIndex::UseIfExists(uint64_t entry_hash) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // Always update the last used time, even if it is during initialization. // It will be merged later. auto it = entries_set_.find(entry_hash); @@ -410,7 +410,7 @@ } void SimpleIndex::StartEvictionIfNeeded() { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (eviction_in_progress_ || cache_size_ <= high_watermark_) return; // Take all live key hashes from the index and sort them by time. @@ -467,7 +467,7 @@ } int32_t SimpleIndex::GetTrailerPrefetchSize(uint64_t entry_hash) const { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_EQ(cache_type_, net::APP_CACHE); auto it = entries_set_.find(entry_hash); if (it == entries_set_.end()) @@ -476,7 +476,7 @@ } void SimpleIndex::SetTrailerPrefetchSize(uint64_t entry_hash, int32_t size) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_EQ(cache_type_, net::APP_CACHE); auto it = entries_set_.find(entry_hash); if (it == entries_set_.end()) @@ -489,7 +489,7 @@ bool SimpleIndex::UpdateEntrySize(uint64_t entry_hash, base::StrictNumeric<uint32_t> entry_size) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); auto it = entries_set_.find(entry_hash); if (it == entries_set_.end()) return false; @@ -505,7 +505,7 @@ } void SimpleIndex::EvictionDone(int result) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // Ignore the result of eviction. We did our best. eviction_in_progress_ = false; @@ -549,7 +549,7 @@ EntrySet::iterator* it, base::StrictNumeric<uint32_t> entry_size) { // Update the total cache size with the new entry size. - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_GE(cache_size_, (*it)->second.GetEntrySize()); uint32_t original_size = (*it)->second.GetEntrySize(); cache_size_ -= (*it)->second.GetEntrySize(); @@ -563,7 +563,7 @@ void SimpleIndex::MergeInitializingSet( std::unique_ptr<SimpleIndexLoadResult> load_result) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); EntrySet* index_file_entries = &load_result->entries; @@ -619,7 +619,7 @@ for (auto it = to_run_when_initialized_.begin(), end = to_run_when_initialized_.end(); it != end; ++it) { - io_thread_->PostTask(FROM_HERE, base::BindOnce(std::move(*it), net::OK)); + task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(*it), net::OK)); } to_run_when_initialized_.clear(); } @@ -627,7 +627,7 @@ #if defined(OS_ANDROID) void SimpleIndex::OnApplicationStateChange( base::android::ApplicationState state) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // For more info about android activities, see: // developer.android.com/training/basics/activity-lifecycle/pausing.html if (state == base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) { @@ -641,7 +641,7 @@ #endif void SimpleIndex::WriteToDisk(IndexWriteToDiskReason reason) { - DCHECK(io_thread_checker_.CalledOnValidThread()); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!initialized_) return;
diff --git a/net/disk_cache/simple/simple_index.h b/net/disk_cache/simple/simple_index.h index c3e3e02..f021cf1 100644 --- a/net/disk_cache/simple/simple_index.h +++ b/net/disk_cache/simple/simple_index.h
@@ -20,8 +20,8 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/numerics/safe_conversions.h" -#include "base/single_thread_task_runner.h" -#include "base/threading/thread_checker.h" +#include "base/sequence_checker.h" +#include "base/sequenced_task_runner.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "build/build_config.h" @@ -134,7 +134,7 @@ typedef std::vector<uint64_t> HashList; - SimpleIndex(const scoped_refptr<base::SingleThreadTaskRunner>& io_thread, + SimpleIndex(const scoped_refptr<base::SequencedTaskRunner>& task_runner, scoped_refptr<BackendCleanupTracker> cleanup_tracker, SimpleIndexDelegate* delegate, net::CacheType cache_type, @@ -289,11 +289,12 @@ std::unique_ptr<SimpleIndexFile> index_file_; - scoped_refptr<base::SingleThreadTaskRunner> io_thread_; + scoped_refptr<base::SequencedTaskRunner> task_runner_; - // All nonstatic SimpleEntryImpl methods should always be called on the IO - // thread, in all cases. |io_thread_checker_| documents and enforces this. - base::ThreadChecker io_thread_checker_; + // All nonstatic SimpleEntryImpl methods should always be called on its + // creation sequance, in all cases. |sequence_checker_| documents and + // enforces this. + SEQUENCE_CHECKER(sequence_checker_); // Timestamp of the last time we wrote the index to disk. // PostponeWritingToDisk() may give up postponing and allow the write if it
diff --git a/net/disk_cache/simple/simple_index_file.h b/net/disk_cache/simple/simple_index_file.h index b8272fb5..14e2da4 100644 --- a/net/disk_cache/simple/simple_index_file.h +++ b/net/disk_cache/simple/simple_index_file.h
@@ -48,7 +48,7 @@ // the format see |SimpleIndexFile::Serialize()| and // |SimpleIndexFile::LoadFromDisk()|. // -// The non-static methods must run on the IO thread. All the real +// The non-static methods must run on the source creation sequence. All the real // work is done in the static methods, which are run on the cache thread // or in worker threads. Synchronization between methods is the // responsibility of the caller. @@ -121,7 +121,8 @@ int64_t size)>; // When loading the entries from disk, add this many extra hash buckets to - // prevent reallocation on the IO thread when merging in new live entries. + // prevent reallocation on the creation sequence when merging in new live + // entries. static const int kExtraSizeForMerge = 512; // Synchronous (IO performing) implementation of LoadIndexEntries.
diff --git a/services/device/wake_lock/power_save_blocker/OWNERS b/services/device/wake_lock/power_save_blocker/OWNERS index 2fddade4..fdd793c 100644 --- a/services/device/wake_lock/power_save_blocker/OWNERS +++ b/services/device/wake_lock/power_save_blocker/OWNERS
@@ -1,5 +1,4 @@ boliu@chromium.org hashimoto@chromium.org -per-file power_save_blocker_chromeos.cc=derat@chromium.org # COMPONENT: Internals>Core
diff --git a/services/identity/public/cpp/identity_manager.cc b/services/identity/public/cpp/identity_manager.cc index 12bd2ad..7bdf6ab 100644 --- a/services/identity/public/cpp/identity_manager.cc +++ b/services/identity/public/cpp/identity_manager.cc
@@ -8,7 +8,6 @@ #include "build/build_config.h" #include "components/signin/core/browser/account_fetcher_service.h" -#include "components/signin/core/browser/gaia_cookie_manager_service.h" #include "components/signin/core/browser/ubertoken_fetcher_impl.h" #include "google_apis/gaia/gaia_auth_util.h" #include "services/identity/public/cpp/accounts_cookie_mutator.h" @@ -66,15 +65,7 @@ token_service_->AddDiagnosticsObserver(this); token_service_->AddObserver(this); account_tracker_service_->AddObserver(this); - - // IdentityManager owns gaia_cookie_manager_service_ and will outlive it, so - // base::Unretained is safe. - gaia_cookie_manager_service_->SetGaiaAccountsInCookieUpdatedCallback( - base::BindRepeating(&IdentityManager::OnGaiaAccountsInCookieUpdated, - base::Unretained(this))); - gaia_cookie_manager_service_->SetGaiaCookieDeletedByUserActionCallback( - base::BindRepeating(&IdentityManager::OnGaiaCookieDeletedByUserAction, - base::Unretained(this))); + gaia_cookie_manager_service_->AddObserver(this); // Seed the primary account with any state that |signin_manager_| loaded from // prefs. @@ -95,6 +86,7 @@ token_service_->RemoveObserver(this); token_service_->RemoveDiagnosticsObserver(this); account_tracker_service_->RemoveObserver(this); + gaia_cookie_manager_service_->RemoveObserver(this); } // TODO(862619) change return type to base::Optional<CoreAccountInfo>
diff --git a/services/identity/public/cpp/identity_manager.h b/services/identity/public/cpp/identity_manager.h index 1fe4c55..7f575e6 100644 --- a/services/identity/public/cpp/identity_manager.h +++ b/services/identity/public/cpp/identity_manager.h
@@ -13,6 +13,7 @@ #include "components/signin/core/browser/account_fetcher_service.h" #include "components/signin/core/browser/account_info.h" #include "components/signin/core/browser/account_tracker_service.h" +#include "components/signin/core/browser/gaia_cookie_manager_service.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" #include "components/signin/core/browser/signin_manager_base.h" #include "components/signin/core/browser/signin_metrics.h" @@ -35,8 +36,6 @@ class PrefRegistrySimple; class SigninManagerAndroid; -class GaiaCookieManagerService; - namespace identity { class AccountsMutator; @@ -53,6 +52,7 @@ class IdentityManager : public SigninManagerBase::Observer, public OAuth2TokenService::DiagnosticsObserver, public OAuth2TokenService::Observer, + public GaiaCookieManagerService::Observer, public AccountTrackerService::Observer { public: class Observer { @@ -596,12 +596,12 @@ void OnAuthErrorChanged(const std::string& account_id, const GoogleServiceAuthError& auth_error) override; - // GaiaCookieManagerService callbacks: + // GaiaCookieManagerService::Observer: void OnGaiaAccountsInCookieUpdated( const std::vector<gaia::ListedAccount>& signed_in_accounts, const std::vector<gaia::ListedAccount>& signed_out_accounts, - const GoogleServiceAuthError& error); - void OnGaiaCookieDeletedByUserAction(); + const GoogleServiceAuthError& error) override; + void OnGaiaCookieDeletedByUserAction() override; // OAuth2TokenService::DiagnosticsObserver: void OnAccessTokenRequested(
diff --git a/services/identity/public/cpp/identity_test_utils.cc b/services/identity/public/cpp/identity_test_utils.cc index 5e94362..641f11e 100644 --- a/services/identity/public/cpp/identity_test_utils.cc +++ b/services/identity/public/cpp/identity_test_utils.cc
@@ -10,7 +10,6 @@ #include "base/run_loop.h" #include "base/strings/string_split.h" #include "components/signin/core/browser/account_tracker_service.h" -#include "components/signin/core/browser/gaia_cookie_manager_service.h" #include "components/signin/core/browser/list_accounts_test_utils.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" #include "google_apis/gaia/gaia_auth_util.h"
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 54b4ee5..57a9145 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -3460,6 +3460,13 @@ "libANGLE" ] }, + "android-code-coverage": { + "junit_tests": [ + { + "test": "base_junit_tests" + } + ] + }, "android-mojo-webview-rel": { "gtest_tests": [ {
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json index c4db86f..e62cbb2 100644 --- a/testing/buildbot/chromium.perf.fyi.json +++ b/testing/buildbot/chromium.perf.fyi.json
@@ -33,7 +33,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 3 }, "trigger_script": { @@ -77,7 +77,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800 + "io_timeout": 14400 }, "trigger_script": { "args": [
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json index 8fef580d3..7cd5178 100644 --- a/testing/buildbot/chromium.perf.json +++ b/testing/buildbot/chromium.perf.json
@@ -30,7 +30,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -69,7 +69,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -108,7 +108,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -151,7 +151,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 16 }, "trigger_script": { @@ -198,7 +198,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 16 }, "trigger_script": { @@ -245,7 +245,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 12 }, "trigger_script": { @@ -286,7 +286,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -323,7 +323,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -360,7 +360,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -399,7 +399,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -438,7 +438,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -479,7 +479,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 5 }, "trigger_script": { @@ -520,7 +520,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -557,7 +557,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -594,7 +594,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -634,7 +634,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 5 }, "trigger_script": { @@ -676,7 +676,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800 + "io_timeout": 14400 } }, { @@ -700,7 +700,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800 + "io_timeout": 14400 } }, { @@ -724,7 +724,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800 + "io_timeout": 14400 } }, { @@ -748,7 +748,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800 + "io_timeout": 14400 } } ] @@ -785,7 +785,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 19 }, "trigger_script": { @@ -831,7 +831,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 25 }, "trigger_script": { @@ -875,7 +875,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -914,7 +914,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -953,7 +953,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -992,7 +992,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1031,7 +1031,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1070,7 +1070,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1113,7 +1113,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 16 }, "trigger_script": { @@ -1159,7 +1159,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 35 }, "trigger_script": { @@ -1205,7 +1205,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 28 }, "trigger_script": { @@ -1247,7 +1247,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800 + "io_timeout": 14400 } }, { @@ -1271,7 +1271,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800 + "io_timeout": 14400 } }, { @@ -1295,7 +1295,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800 + "io_timeout": 14400 } }, { @@ -1319,7 +1319,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800 + "io_timeout": 14400 } } ] @@ -1356,7 +1356,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1393,7 +1393,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1430,7 +1430,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1467,7 +1467,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1504,7 +1504,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1541,7 +1541,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1582,7 +1582,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 26 }, "trigger_script": { @@ -1623,7 +1623,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1660,7 +1660,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1701,7 +1701,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 26 }, "trigger_script": { @@ -1742,7 +1742,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1779,7 +1779,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1816,7 +1816,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1853,7 +1853,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1890,7 +1890,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -1931,7 +1931,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 26 }, "trigger_script": { @@ -1978,7 +1978,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -2015,7 +2015,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -2052,7 +2052,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -2089,7 +2089,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -2126,7 +2126,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 1 }, "trigger_script": { @@ -2167,7 +2167,7 @@ "expiration": 7200, "hard_timeout": 36000, "ignore_task_failure": false, - "io_timeout": 1800, + "io_timeout": 14400, "shards": 26 }, "trigger_script": {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index 4f8718bb..a997b7a8 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -24,6 +24,10 @@ 'monochrome_public_test_ar_apk': {}, }, + 'android_code_coverage_junit_tests': { + 'base_junit_tests': {}, + }, + 'android_ddready_vr_gtests': { 'chrome_public_test_vr_apk-ddready-cardboard': { 'args': [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl index 9e42a9b1..4e975d1 100644 --- a/testing/buildbot/waterfalls.pyl +++ b/testing/buildbot/waterfalls.pyl
@@ -1598,6 +1598,14 @@ 'libANGLE', ], }, + 'android-code-coverage': { + 'mixins': [ + 'code-coverage', + ], + 'test_suites': { + 'junit_tests': 'android_code_coverage_junit_tests', + } + }, 'android-mojo-webview-rel': { 'swarming': { 'dimension_sets': [
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 7160184a..04a3d5546 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -1874,6 +1874,24 @@ ] } ], + "DownloadResumptionWithoutStrongValidators": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "Enabled", + "params": { + "download_validation_length": "1024" + }, + "enable_features": [ + "AllowDownloadResumptionWithoutStrongValidators" + ] + } + ] + } + ], "DownloadsLocationChange": [ { "platforms": [
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn index 7a4a457..f87dc3b 100644 --- a/third_party/blink/renderer/core/BUILD.gn +++ b/third_party/blink/renderer/core/BUILD.gn
@@ -2037,6 +2037,7 @@ "loader/frame_fetch_context_test.cc", "loader/frame_resource_fetcher_properties_test.cc", "loader/idleness_detector_test.cc", + "loader/image_loader_test.cc", "loader/interactive_detector_test.cc", "loader/link_loader_test.cc", "loader/long_task_detector_test.cc",
diff --git a/third_party/blink/renderer/core/css/css_paint_value.cc b/third_party/blink/renderer/core/css/css_paint_value.cc index d669c5e..e19c886 100644 --- a/third_party/blink/renderer/core/css/css_paint_value.cc +++ b/third_party/blink/renderer/core/css/css_paint_value.cc
@@ -67,7 +67,6 @@ // For Off-Thread PaintWorklet, we just collect the necessary inputs together // and defer the actual JavaScript call until much later (during cc Raster). if (RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled()) { - // TODO(crbug.com/946515): Break dependency on LayoutObject. const LayoutObject& layout_object = static_cast<const LayoutObject&>(client);
diff --git a/third_party/blink/renderer/core/css/css_syntax_descriptor.h b/third_party/blink/renderer/core/css/css_syntax_descriptor.h index ba332a8..6a819763 100644 --- a/third_party/blink/renderer/core/css/css_syntax_descriptor.h +++ b/third_party/blink/renderer/core/css/css_syntax_descriptor.h
@@ -7,6 +7,7 @@ #include "third_party/blink/renderer/core/css/css_syntax_component.h" #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h" +#include "third_party/blink/renderer/platform/cross_thread_copier.h" namespace blink { @@ -48,6 +49,19 @@ Vector<CSSSyntaxComponent> syntax_components_; }; +template <wtf_size_t inlineCapacity, typename Allocator> +struct CrossThreadCopier< + Vector<CSSSyntaxDescriptor, inlineCapacity, Allocator>> { + using Type = Vector<CSSSyntaxDescriptor, inlineCapacity, Allocator>; + static Type Copy(const Type& value) { + Type result; + result.ReserveInitialCapacity(value.size()); + for (const auto& element : value) + result.push_back(element.IsolatedCopy()); + return result; + } +}; + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_SYNTAX_DESCRIPTOR_H_
diff --git a/third_party/blink/renderer/core/css/media_values_cached.h b/third_party/blink/renderer/core/css/media_values_cached.h index 96df16f..a9dfbd9e 100644 --- a/third_party/blink/renderer/core/css/media_values_cached.h +++ b/third_party/blink/renderer/core/css/media_values_cached.h
@@ -113,9 +113,7 @@ template <> struct CrossThreadCopier<MediaValuesCached::MediaValuesCachedData> { typedef MediaValuesCached::MediaValuesCachedData Type; - static Type Copy(const MediaValuesCached::MediaValuesCachedData& data) { - return data.DeepCopy(); - } + static Type Copy(const Type& data) { return data.DeepCopy(); } }; } // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc index 9278785..173a46c 100644 --- a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc +++ b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
@@ -211,8 +211,8 @@ if (!BackgroundLayerMayBeSprite(*background_layer)) { if (element_->GetDocument() .GetFrame() - ->GetLazyLoadImageEnabledState() == - LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic) { + ->GetLazyLoadImageSetting() == + LocalFrame::LazyLoadImageSetting::kEnabledAutomatic) { image_request_optimization = FetchParameters::kDeferImageLoad; } else { image_request_optimization = FetchParameters::kAllowPlaceholder;
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index cee875e..d055e4a 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2218,8 +2218,8 @@ LegacyLayout legacy = children_context.force_legacy_layout ? LegacyLayout::kForce : LegacyLayout::kAuto; - LayoutTreeBuilderForElement builder(*this, style); - builder.CreateLayoutObjectIfNeeded(legacy); + LayoutTreeBuilderForElement builder(*this, style, legacy); + builder.CreateLayoutObjectIfNeeded(); } LayoutObject* layout_object = GetLayoutObject();
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder.cc b/third_party/blink/renderer/core/dom/layout_tree_builder.cc index 8aff31dc..00707b2f 100644 --- a/third_party/blink/renderer/core/dom/layout_tree_builder.cc +++ b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
@@ -47,8 +47,9 @@ LayoutTreeBuilderForElement::LayoutTreeBuilderForElement( Element& element, - const ComputedStyle* style) - : LayoutTreeBuilder(element, nullptr, style) { + const ComputedStyle* style, + LegacyLayout legacy) + : LayoutTreeBuilder(element, nullptr, style), legacy_(legacy) { DCHECK(element.CanParticipateInFlatTree()); DCHECK(style_); DCHECK(!style_->IsEnsuredInDisplayNone()); @@ -56,8 +57,11 @@ // It's an extra (unnecessary) check for text nodes, though. if (element.IsFirstLetterPseudoElement()) { if (LayoutObject* next_layout_object = - FirstLetterPseudoElement::FirstLetterTextLayoutObject(element)) + FirstLetterPseudoElement::FirstLetterTextLayoutObject(element)) { layout_object_parent_ = next_layout_object->Parent(); + if (layout_object_parent_->ForceLegacyLayout()) + legacy_ = LegacyLayout::kForce; + } } else { layout_object_parent_ = LayoutTreeBuilderTraversal::ParentLayoutObject(element); @@ -107,8 +111,8 @@ } DISABLE_CFI_PERF -void LayoutTreeBuilderForElement::CreateLayoutObject(LegacyLayout legacy) { - LayoutObject* new_layout_object = node_->CreateLayoutObject(*style_, legacy); +void LayoutTreeBuilderForElement::CreateLayoutObject() { + LayoutObject* new_layout_object = node_->CreateLayoutObject(*style_, legacy_); if (!new_layout_object) return;
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder.h b/third_party/blink/renderer/core/dom/layout_tree_builder.h index 37a499e..201d386 100644 --- a/third_party/blink/renderer/core/dom/layout_tree_builder.h +++ b/third_party/blink/renderer/core/dom/layout_tree_builder.h
@@ -101,18 +101,22 @@ class LayoutTreeBuilderForElement : public LayoutTreeBuilder<Element> { public: - LayoutTreeBuilderForElement(Element&, const ComputedStyle*); + LayoutTreeBuilderForElement(Element&, + const ComputedStyle*, + LegacyLayout legacy); - void CreateLayoutObjectIfNeeded(LegacyLayout legacy) { + void CreateLayoutObjectIfNeeded() { if (ShouldCreateLayoutObject()) - CreateLayoutObject(legacy); + CreateLayoutObject(); } private: LayoutObject* ParentLayoutObject() const; LayoutObject* NextLayoutObject() const; bool ShouldCreateLayoutObject() const; - void CreateLayoutObject(LegacyLayout); + void CreateLayoutObject(); + + LegacyLayout legacy_; }; class LayoutTreeBuilderForText : public LayoutTreeBuilder<Text> {
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc index d1922a48..bdf2c291 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1290,23 +1290,22 @@ request, Client()->GetPreviewsStateForFrame()); } -LocalFrame::LazyLoadImageEnabledState LocalFrame::GetLazyLoadImageEnabledState() - const { +LocalFrame::LazyLoadImageSetting LocalFrame::GetLazyLoadImageSetting() const { DCHECK(GetSettings()); if (!RuntimeEnabledFeatures::LazyImageLoadingEnabled() || !GetSettings()->GetLazyLoadEnabled()) { - return LocalFrame::LazyLoadImageEnabledState::kDisabled; + return LocalFrame::LazyLoadImageSetting::kDisabled; } if (!RuntimeEnabledFeatures::AutomaticLazyImageLoadingEnabled()) - return LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit; + return LocalFrame::LazyLoadImageSetting::kEnabledExplicit; if (RuntimeEnabledFeatures:: RestrictAutomaticLazyImageLoadingToDataSaverEnabled() && !is_save_data_enabled_) { - return LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit; + return LocalFrame::LazyLoadImageSetting::kEnabledExplicit; } if (Owner() && !Owner()->ShouldLazyLoadChildren()) - return LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit; - return LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic; + return LocalFrame::LazyLoadImageSetting::kEnabledExplicit; + return LocalFrame::LazyLoadImageSetting::kEnabledAutomatic; } WebURLLoaderFactory* LocalFrame::GetURLLoaderFactory() {
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h index 784ff44b..f9d2ce6 100644 --- a/third_party/blink/renderer/core/frame/local_frame.h +++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -317,13 +317,13 @@ // Returns true if Client Lo-Fi should be used for this request. bool IsClientLoFiAllowed(const ResourceRequest&) const; - enum class LazyLoadImageEnabledState { + enum class LazyLoadImageSetting { kDisabled, kEnabledExplicit, kEnabledAutomatic }; // Returns the enabled state of lazyloading of images. - LazyLoadImageEnabledState GetLazyLoadImageEnabledState() const; + LazyLoadImageSetting GetLazyLoadImageSetting() const; // The returned value is a off-heap raw-ptr and should not be stored. WebURLLoaderFactory* GetURLLoaderFactory();
diff --git a/third_party/blink/renderer/core/frame/local_frame_test.cc b/third_party/blink/renderer/core/frame/local_frame_test.cc index 7bf062b..b9507054 100644 --- a/third_party/blink/renderer/core/frame/local_frame_test.cc +++ b/third_party/blink/renderer/core/frame/local_frame_test.cc
@@ -187,8 +187,8 @@ auto page_holder = std::make_unique<DummyPageHolder>( IntSize(800, 600), nullptr, nullptr, &EnableLazyLoadAndDisableDataSaverHoldbackInSettings); - EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kDisabled, - page_holder->GetFrame().GetLazyLoadImageEnabledState()); + EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kDisabled, + page_holder->GetFrame().GetLazyLoadImageSetting()); } TEST_F(LocalFrameTest, IsLazyLoadingImageAllowedWithSettingDisabled) { @@ -196,8 +196,8 @@ auto page_holder = std::make_unique<DummyPageHolder>( IntSize(800, 600), nullptr, nullptr, &DisableLazyLoadAndDisableDataSaverHoldbackInSettings); - EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kDisabled, - page_holder->GetFrame().GetLazyLoadImageEnabledState()); + EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kDisabled, + page_holder->GetFrame().GetLazyLoadImageSetting()); } TEST_F(LocalFrameTest, IsLazyLoadingImageAllowedWithAutomaticDisabled) { @@ -207,8 +207,8 @@ auto page_holder = std::make_unique<DummyPageHolder>( IntSize(800, 600), nullptr, nullptr, &EnableLazyLoadAndDisableDataSaverHoldbackInSettings); - EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit, - page_holder->GetFrame().GetLazyLoadImageEnabledState()); + EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kEnabledExplicit, + page_holder->GetFrame().GetLazyLoadImageSetting()); } TEST_F(LocalFrameTest, IsLazyLoadingImageAllowedWhenNotRestricted) { @@ -221,8 +221,8 @@ auto page_holder = std::make_unique<DummyPageHolder>( IntSize(800, 600), nullptr, nullptr, &EnableLazyLoadAndDisableDataSaverHoldbackInSettings); - EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic, - page_holder->GetFrame().GetLazyLoadImageEnabledState()); + EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kEnabledAutomatic, + page_holder->GetFrame().GetLazyLoadImageSetting()); } TEST_F(LocalFrameTest, @@ -236,8 +236,8 @@ auto page_holder = std::make_unique<DummyPageHolder>( IntSize(800, 600), nullptr, nullptr, &EnableLazyLoadAndDisableDataSaverHoldbackInSettings); - EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit, - page_holder->GetFrame().GetLazyLoadImageEnabledState()); + EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kEnabledExplicit, + page_holder->GetFrame().GetLazyLoadImageSetting()); } TEST_F(LocalFrameTest, @@ -251,8 +251,8 @@ auto page_holder = std::make_unique<DummyPageHolder>( IntSize(800, 600), nullptr, nullptr, &EnableLazyLoadAndEnableDataSaverHoldbackInSettings); - EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledExplicit, - page_holder->GetFrame().GetLazyLoadImageEnabledState()); + EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kEnabledExplicit, + page_holder->GetFrame().GetLazyLoadImageSetting()); } TEST_F(LocalFrameTest, @@ -266,8 +266,8 @@ auto page_holder = std::make_unique<DummyPageHolder>( IntSize(800, 600), nullptr, nullptr, &EnableLazyLoadAndDisableDataSaverHoldbackInSettings); - EXPECT_EQ(LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic, - page_holder->GetFrame().GetLazyLoadImageEnabledState()); + EXPECT_EQ(LocalFrame::LazyLoadImageSetting::kEnabledAutomatic, + page_holder->GetFrame().GetLazyLoadImageSetting()); } } // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc index c5f40e72..7f0fba4 100644 --- a/third_party/blink/renderer/core/html/html_image_element.cc +++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -448,8 +448,8 @@ } } - if (image_was_modified || - GetImageLoader().ShouldUpdateOnInsertedInto(insertion_point)) { + if (image_was_modified || GetImageLoader().ShouldUpdateOnInsertedInto( + insertion_point, referrer_policy_)) { GetImageLoader().UpdateFromElement(ImageLoader::kUpdateNormal, referrer_policy_); }
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc index 826124fb..da197da 100644 --- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc +++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
@@ -306,8 +306,8 @@ break; case LoadingAttrValue::kLazy: is_lazy_load_image_enabled = - document_parameters.lazy_load_image_enabled_state != - LocalFrame::LazyLoadImageEnabledState::kDisabled; + document_parameters.lazy_load_image_setting != + LocalFrame::LazyLoadImageSetting::kDisabled; break; case LoadingAttrValue::kAuto: if ((width_attr_dimension_type_ == @@ -319,8 +319,8 @@ is_lazy_load_image_enabled = false; } else { is_lazy_load_image_enabled = - document_parameters.lazy_load_image_enabled_state == - LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic; + document_parameters.lazy_load_image_setting == + LocalFrame::LazyLoadImageSetting::kEnabledAutomatic; } break; } @@ -1087,11 +1087,10 @@ integrity_features = SubresourceIntegrityHelper::GetFeatures(document); lazyload_policy_enforced = document->IsLazyLoadPolicyEnforced(); if (document->Loader() && document->Loader()->GetFrame()) { - lazy_load_image_enabled_state = - document->Loader()->GetFrame()->GetLazyLoadImageEnabledState(); + lazy_load_image_setting = + document->Loader()->GetFrame()->GetLazyLoadImageSetting(); } else { - lazy_load_image_enabled_state = - LocalFrame::LazyLoadImageEnabledState::kDisabled; + lazy_load_image_setting = LocalFrame::LazyLoadImageSetting::kDisabled; } }
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.h b/third_party/blink/renderer/core/html/parser/html_preload_scanner.h index 90c3cfc8..d621de64 100644 --- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.h +++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.h
@@ -56,12 +56,6 @@ USING_FAST_MALLOC(CachedDocumentParameters); public: - enum class LazyLoadImageEnabledState { - kDisabled, - kEnabledExplicit, - kEnabledAutomatic - }; - explicit CachedDocumentParameters(Document*); CachedDocumentParameters() = default; @@ -72,7 +66,7 @@ network::mojom::ReferrerPolicy referrer_policy; SubresourceIntegrity::IntegrityFeatures integrity_features; bool lazyload_policy_enforced; - LocalFrame::LazyLoadImageEnabledState lazy_load_image_enabled_state; + LocalFrame::LazyLoadImageSetting lazy_load_image_setting; }; class TokenPreloadScanner {
diff --git a/third_party/blink/renderer/core/layout/jank_tracker.cc b/third_party/blink/renderer/core/layout/jank_tracker.cc index 18fc48f..1fda05d 100644 --- a/third_party/blink/renderer/core/layout/jank_tracker.cc +++ b/third_party/blink/renderer/core/layout/jank_tracker.cc
@@ -71,9 +71,8 @@ rect.Height() * granularity_scale < 0.5; } -static const TransformPaintPropertyNode& TransformNodeFor( - LayoutObject& object) { - return object.FirstFragment().LocalBorderBoxProperties().Transform(); +static const PropertyTreeState PropertyTreeStateFor(LayoutObject& object) { + return object.FirstFragment().LocalBorderBoxProperties(); } static void RegionToTracedValue(const Region& region, @@ -150,34 +149,54 @@ if (source.IsSVG()) return; - const auto& local_xform = TransformNodeFor(painting_layer.GetLayoutObject()); - const auto& root_xform = TransformNodeFor(*source.View()); + const auto local_state = + PropertyTreeStateFor(painting_layer.GetLayoutObject()); + const auto root_state = PropertyTreeStateFor(*source.View()); - GeometryMapper::SourceToDestinationRect(local_xform, root_xform, old_rect); - GeometryMapper::SourceToDestinationRect(local_xform, root_xform, new_rect); + FloatClipRect clip_rect = + GeometryMapper::LocalToAncestorClipRect(local_state, root_state); - if (!old_rect.Intersects(viewport) && !new_rect.Intersects(viewport)) + // If the clip region is empty, then the resulting layout shift isn't visible + // in the viewport so ignore it. + if (!clip_rect.IsInfinite() && clip_rect.Rect().IsEmpty()) return; + GeometryMapper::SourceToDestinationRect(local_state.Transform(), + root_state.Transform(), old_rect); + GeometryMapper::SourceToDestinationRect(local_state.Transform(), + root_state.Transform(), new_rect); + + FloatRect clipped_old_rect(old_rect), clipped_new_rect(new_rect); + if (!clip_rect.IsInfinite()) { + clipped_old_rect.Intersect(clip_rect.Rect()); + clipped_new_rect.Intersect(clip_rect.Rect()); + } + + IntRect visible_old_rect = RoundedIntRect(clipped_old_rect); + visible_old_rect.Intersect(viewport); + IntRect visible_new_rect = RoundedIntRect(clipped_new_rect); + visible_new_rect.Intersect(viewport); + + if (visible_old_rect.IsEmpty() && visible_new_rect.IsEmpty()) + return; + + // Compute move distance based on unclipped rects, to accurately determine how + // much the element moved. + float move_distance = GetMoveDistance(old_rect, new_rect, source); + frame_max_distance_ = std::max(frame_max_distance_, move_distance); + #if DCHECK_IS_ON() LocalFrame& frame = frame_view_->GetFrame(); if (ShouldLog(frame)) { DVLOG(2) << "in " << (frame.IsMainFrame() ? "" : "subframe ") << frame.GetDocument()->Url().GetString() << ", " << source.DebugName() << " moved from " << old_rect.ToString() - << " to " << new_rect.ToString(); + << " to " << new_rect.ToString() << " (visible from " + << visible_old_rect.ToString() << " to " + << visible_new_rect.ToString() << ")"; } #endif - frame_max_distance_ = std::max(frame_max_distance_, - GetMoveDistance(old_rect, new_rect, source)); - - IntRect visible_old_rect = RoundedIntRect(old_rect); - visible_old_rect.Intersect(viewport); - - IntRect visible_new_rect = RoundedIntRect(new_rect); - visible_new_rect.Intersect(viewport); - visible_old_rect.Scale(scale); visible_new_rect.Scale(scale);
diff --git a/third_party/blink/renderer/core/layout/jank_tracker_test.cc b/third_party/blink/renderer/core/layout/jank_tracker_test.cc index ed95372..78959ec 100644 --- a/third_party/blink/renderer/core/layout/jank_tracker_test.cc +++ b/third_party/blink/renderer/core/layout/jank_tracker_test.cc
@@ -276,6 +276,96 @@ EXPECT_FLOAT_EQ(0.1, GetJankTracker().Score()); } +TEST_F(JankTrackerTest, FullyClippedVisualRect) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + #clip { width: 0px; height: 600px; overflow: hidden; } + #j { position: relative; width: 300px; height: 200px; } + </style> + <div id='clip'><div id='j'></div></div> + )HTML"); + + GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr, + AtomicString("top: 200px")); + UpdateAllLifecyclePhases(); + EXPECT_FLOAT_EQ(0.0, GetJankTracker().Score()); +} + +TEST_F(JankTrackerTest, PartiallyClippedVisualRect) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + #clip { width: 150px; height: 600px; overflow: hidden; } + #j { position: relative; width: 300px; height: 200px; } + </style> + <div id='clip'><div id='j'></div></div> + )HTML"); + + GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr, + AtomicString("top: 200px")); + UpdateAllLifecyclePhases(); + // (clipped width 150px) * (height 200 + movement 200) / (800 * 600 viewport) + EXPECT_FLOAT_EQ(0.125, GetJankTracker().Score()); +} + +TEST_F(JankTrackerTest, MultiClipVisualRect) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + #outer { width: 200px; height: 600px; overflow: hidden; } + #inner { width: 300px; height: 150px; overflow: hidden; } + #j { position: relative; width: 300px; height: 600px; } + </style> + <div id='outer'><div id='inner'><div id='j'></div></div></div> + )HTML"); + + GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr, + AtomicString("top: -200px")); + UpdateAllLifecyclePhases(); + // Note that, while the element moves up 200px, its visibility is + // clipped at 0px,150px height, so the additional 200px of vertical + // move distance is not included in the score. + // (clip width 200) * (clip height 150) / (800 * 600 viewport) + EXPECT_FLOAT_EQ(0.0625, GetJankTracker().Score()); + EXPECT_FLOAT_EQ(200.0, GetJankTracker().OverallMaxDistance()); +} + +TEST_F(JankTrackerTest, ShiftOutsideViewport) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + #j { position: relative; width: 600px; height: 200px; top: 600px; } + </style> + <div id='j'></div> + )HTML"); + + GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr, + AtomicString("top: 800px")); + UpdateAllLifecyclePhases(); + // Since the element moves entirely outside of the viewport, it shouldn't + // generate a score. + EXPECT_FLOAT_EQ(0.0, GetJankTracker().Score()); +} + +TEST_F(JankTrackerTest, ShiftInToViewport) { + SetBodyInnerHTML(R"HTML( + <style> + body { margin: 0; } + #j { position: relative; width: 600px; height: 200px; top: 600px; } + </style> + <div id='j'></div> + )HTML"); + + GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr, + AtomicString("top: 400px")); + UpdateAllLifecyclePhases(); + // The element moves from outside the viewport to within the viewport, which + // should generate jank. + // (width 600) * (height 0 + move 200) / (800 * 600 viewport) + EXPECT_FLOAT_EQ(0.25, GetJankTracker().Score()); +} + class JankTrackerSimTest : public SimTest {}; TEST_F(JankTrackerSimTest, SubframeWeighting) {
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc index dabbe34..dac82df 100644 --- a/third_party/blink/renderer/core/loader/image_loader.cc +++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -343,7 +343,8 @@ } bool ImageLoader::ShouldUpdateOnInsertedInto( - ContainerNode& insertion_point) const { + ContainerNode& insertion_point, + network::mojom::ReferrerPolicy referrer_policy) const { // If we're being inserted into a disconnected tree, we don't need to update. if (!insertion_point.isConnected()) return false; @@ -355,10 +356,15 @@ if (element_->GetDocument().ValidBaseElementURL() != last_base_element_url_) return true; - // Finally, try to update if we're idle (that is, we have neither the image - // contents nor any activity). This could be an indication that we skipped a - // previous load when inserted into an inactive document. - return !image_content_ && !HasPendingActivity(); + // If we already have image content, then we don't need an update. + if (image_content_) + return false; + + // Finally, try to update if we're idle. This could be an indication that we + // skipped a previous load when inserted into an inactive document. Note that + // if we're not idle, we should also update our referrer policy if it has + // changed. + return !HasPendingActivity() || referrer_policy != last_referrer_policy_; } void ImageLoader::ClearImage() { @@ -562,17 +568,16 @@ const LazyLoadImageEligibility lazy_load_image_eligibility = DetermineLazyLoadImageEligibility(*frame, *html_image, params.Url()); - const auto lazy_load_image_enabled_state = - frame->GetLazyLoadImageEnabledState(); + const auto lazy_load_image_setting = frame->GetLazyLoadImageSetting(); if ((lazy_load_image_eligibility == LazyLoadImageEligibility::kEnabledExplicit && - lazy_load_image_enabled_state != - LocalFrame::LazyLoadImageEnabledState::kDisabled) || + lazy_load_image_setting != + LocalFrame::LazyLoadImageSetting::kDisabled) || (lazy_load_image_eligibility == LazyLoadImageEligibility::kEnabledAutomatic && - lazy_load_image_enabled_state == - LocalFrame::LazyLoadImageEnabledState::kEnabledAutomatic)) { + lazy_load_image_setting == + LocalFrame::LazyLoadImageSetting::kEnabledAutomatic)) { if (IsDimensionAbsoluteLarge(*html_image)) { params.SetLazyImageDeferred(); } else { @@ -659,6 +664,7 @@ suppress_error_events_ = (update_behavior == kUpdateSizeChanged); last_base_element_url_ = element_->GetDocument().ValidBaseElementURL().GetString(); + last_referrer_policy_ = referrer_policy; if (update_behavior == kUpdateIgnorePreviousError) ClearFailedLoadURL();
diff --git a/third_party/blink/renderer/core/loader/image_loader.h b/third_party/blink/renderer/core/loader/image_loader.h index 26093ff..19231a8 100644 --- a/third_party/blink/renderer/core/loader/image_loader.h +++ b/third_party/blink/renderer/core/loader/image_loader.h
@@ -90,7 +90,10 @@ // Returns true if this loader should be updated via UpdateFromElement() when // being inserted into a new parent; returns false otherwise. - bool ShouldUpdateOnInsertedInto(ContainerNode& insertion_point) const; + bool ShouldUpdateOnInsertedInto( + ContainerNode& insertion_point, + network::mojom::ReferrerPolicy referrer_policy = + network::mojom::ReferrerPolicy::kDefault) const; // Cancels pending load events, and doesn't dispatch new ones. // Note: ClearImage/SetImage.*() are not a simple setter. @@ -208,6 +211,8 @@ Member<ImageResource> image_resource_for_image_document_; String last_base_element_url_; + network::mojom::ReferrerPolicy last_referrer_policy_ = + network::mojom::ReferrerPolicy::kDefault; AtomicString failed_load_url_; base::WeakPtr<Task> pending_task_; // owned by Microtask std::unique_ptr<IncrementLoadEventDelayCount>
diff --git a/third_party/blink/renderer/core/loader/image_loader_test.cc b/third_party/blink/renderer/core/loader/image_loader_test.cc new file mode 100644 index 0000000..95d02c94 --- /dev/null +++ b/third_party/blink/renderer/core/loader/image_loader_test.cc
@@ -0,0 +1,52 @@ +// Copyright 2019 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 "third_party/blink/renderer/core/loader/image_loader.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/html/html_image_loader.h" +#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" + +namespace blink { + +class ImageLoaderTest : public RenderingTest {}; + +TEST_F(ImageLoaderTest, ReferrerPolicyChangeCausesUpdateOnInsert) { + SetHtmlInnerHTML(R"HTML( + <img id="test" src="test.png"> + )HTML"); + + auto* element = GetDocument().getElementById("test"); + ASSERT_TRUE(element); + + auto* loader = MakeGarbageCollected<HTMLImageLoader>(element); + ASSERT_TRUE(loader); + + // We should already be collected, so UpdateFromElement() would cause some + // pending activity. + loader->UpdateFromElement(); + ASSERT_TRUE(loader->HasPendingActivity()); + + // We don't need an update, since we're already loading an image. + EXPECT_FALSE(loader->ShouldUpdateOnInsertedInto(*element)); + + // However, if the referrer policy changes, then we should need an update. + EXPECT_TRUE(loader->ShouldUpdateOnInsertedInto( + *element, network::mojom::ReferrerPolicy::kNever)); + + // Changing referrer policy. + loader->UpdateFromElement(ImageLoader::kUpdateNormal, + network::mojom::ReferrerPolicy::kNever); + + // Now, we don't need an update with the latest referrer policy. + EXPECT_FALSE(loader->ShouldUpdateOnInsertedInto( + *element, network::mojom::ReferrerPolicy::kNever)); + + // But we do want an update if the referrer policy changes back to what it was + // before. + EXPECT_TRUE(loader->ShouldUpdateOnInsertedInto( + *element, network::mojom::ReferrerPolicy::kDefault)); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc index ae57e30e..29d5d8f 100644 --- a/third_party/blink/renderer/core/paint/paint_layer.cc +++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -1642,11 +1642,15 @@ void PaintLayer::UpdateScrollableArea() { if (RequiresScrollableArea() && !scrollable_area_) { scrollable_area_ = PaintLayerScrollableArea::Create(*this); - Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree); + if (Compositor()) { + Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree); + } } else if (!RequiresScrollableArea() && scrollable_area_) { scrollable_area_->Dispose(); scrollable_area_.Clear(); - Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree); + if (Compositor()) { + Compositor()->SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree); + } } } @@ -2820,8 +2824,11 @@ // work with CompositeAfterPaint. Some transform tree changes may still // produce incorrect behavior from JankTracker (see discussion on review // thread of http://crrev.com/c/1636403). - Compositor()->ForceRecomputeVisualRectsIncludingNonCompositingDescendants( - layout_object_); + if (Compositor()) { + Compositor() + ->ForceRecomputeVisualRectsIncludingNonCompositingDescendants( + layout_object_); + } } } else { // We need to make sure our decendants get a geometry update. In principle,
diff --git a/third_party/blink/renderer/core/workers/worker_thread_test.cc b/third_party/blink/renderer/core/workers/worker_thread_test.cc index adb1f60..26897d1 100644 --- a/third_party/blink/renderer/core/workers/worker_thread_test.cc +++ b/third_party/blink/renderer/core/workers/worker_thread_test.cc
@@ -23,6 +23,22 @@ #include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" +// TODO(crbug.com/960985): Fix memory leaks in tests and re-enable on LSAN. +#ifdef LEAK_SANITIZER +#define MAYBE_TerminateFrozenScript DISABLED_TerminateFrozenScript +#define MAYBE_NestedPauseFreeze DISABLED_NestedPauseFreeze +#define MAYBE_TerminateWhileWorkerPausedByDebugger \ + DISABLED_TerminateWhileWorkerPausedByDebugger +#define MAYBE_NestedPauseFreezeNoInterrupts \ + DISABLED_NestedPauseFreezeNoInterrupts +#else +#define MAYBE_TerminateFrozenScript TerminateFrozenScript +#define MAYBE_NestedPauseFreeze NestedPauseFreeze +#define MAYBE_TerminateWhileWorkerPausedByDebugger \ + TerminateWhileWorkerPausedByDebugger +#define MAYBE_NestedPauseFreezeNoInterrupts NestedPauseFreezeNoInterrupts +#endif + using testing::_; using testing::AtMost; @@ -514,7 +530,7 @@ } // Tests terminating a worker when debugger is paused. -TEST_F(WorkerThreadTest, TerminateWhileWorkerPausedByDebugger) { +TEST_F(WorkerThreadTest, MAYBE_TerminateWhileWorkerPausedByDebugger) { constexpr TimeDelta kDelay = TimeDelta::FromMilliseconds(10); SetForcibleTerminationDelay(kDelay); @@ -535,7 +551,7 @@ EXPECT_EQ(ExitCode::kAsyncForciblyTerminated, GetExitCode()); } -TEST_F(WorkerThreadTest, TerminateFrozenScript) { +TEST_F(WorkerThreadTest, MAYBE_TerminateFrozenScript) { constexpr TimeDelta kDelay = TimeDelta::FromMilliseconds(10); SetForcibleTerminationDelay(kDelay); @@ -563,7 +579,7 @@ EXPECT_EQ(ExitCode::kAsyncForciblyTerminated, GetExitCode()); } -TEST_F(WorkerThreadTest, NestedPauseFreeze) { +TEST_F(WorkerThreadTest, MAYBE_NestedPauseFreeze) { constexpr TimeDelta kDelay = TimeDelta::FromMilliseconds(10); SetForcibleTerminationDelay(kDelay); @@ -602,7 +618,7 @@ EXPECT_EQ(ExitCode::kAsyncForciblyTerminated, GetExitCode()); } -TEST_F(WorkerThreadTest, NestedPauseFreezeNoInterrupts) { +TEST_F(WorkerThreadTest, MAYBE_NestedPauseFreezeNoInterrupts) { constexpr TimeDelta kDelay = TimeDelta::FromMilliseconds(10); SetForcibleTerminationDelay(kDelay);
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotGridNodes.js b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotGridNodes.js index 584e5791..5443b9c 100644 --- a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotGridNodes.js +++ b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotGridNodes.js
@@ -539,7 +539,7 @@ valueStyle = 'null'; break; case 'array': - value = (value || ls`(internal array)`) + '[]'; + value = value ? `${value}[]` : ls`(internal array)[]`; break; } return this._createObjectCellWithValue(valueStyle, value);
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp b/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp index b46a803..9b5bb21d 100644 --- a/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp +++ b/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp
@@ -159,9 +159,6 @@ <message name="IDS_DEVTOOLS_54afafa921428eed23978f633af4ce99" desc=""> Allocation sampling </message> - <message name="IDS_DEVTOOLS_57b1250c3cbec60c67810ac83afb2c64" desc=""> - (internal array) - </message> <message name="IDS_DEVTOOLS_5dd1d9f5baa173ae9267b3631da17a83" desc=""> Self size </message> @@ -261,6 +258,9 @@ <message name="IDS_DEVTOOLS_9206de0db462c24e8a56433e2f7b7c75" desc=""> Heap Snapshot </message> + <message name="IDS_DEVTOOLS_9274c1c81e169e786d1dc76b464b9089" desc=""> + (internal array)[] + </message> <message name="IDS_DEVTOOLS_959f661cf87130eea5c8e5671c013741" desc=""> Objects allocated before <ph name="LIST_I__TITLE">$1s</ph> </message>
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js b/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js index 6369715..6c91449 100644 --- a/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js +++ b/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js
@@ -1376,9 +1376,10 @@ } _updateTooltip() { - this.tooltip = Common.UIString('Version') + ': ' + this._database.version; if (Object.keys(this._idbObjectStoreTreeElements).length === 0) - this.tooltip += ls` (empty)`; + this.tooltip = ls`Version: ${this._database.version} (empty)`; + else + this.tooltip = ls`Version: ${this._database.version}`; } /** @@ -1510,7 +1511,7 @@ _updateTooltip() { const keyPathString = this._objectStore.keyPathString; - let tooltipString = keyPathString !== null ? (Common.UIString('Key path: ') + keyPathString) : ''; + let tooltipString = keyPathString !== null ? ls`Key path: ${keyPathString}` : ''; if (this._objectStore.autoIncrement) tooltipString += '\n' + Common.UIString('autoIncrement'); this.tooltip = tooltipString; @@ -1603,7 +1604,7 @@ _updateTooltip() { const tooltipLines = []; const keyPathString = this._index.keyPathString; - tooltipLines.push(Common.UIString('Key path: ') + keyPathString); + tooltipLines.push(ls`Key path: ${keyPathString}`); if (this._index.unique) tooltipLines.push(Common.UIString('unique')); if (this._index.multiEntry) @@ -1687,7 +1688,7 @@ super(storagePanel, cookieDomain ? cookieDomain : Common.UIString('Local Files'), false); this._target = frame.resourceTreeModel().target(); this._cookieDomain = cookieDomain; - this.tooltip = ls`cookies used by frames from ` + cookieDomain; + this.tooltip = ls`cookies used by frames from ${cookieDomain}`; const icon = UI.Icon.create('mediumicon-cookie', 'resource-tree-item'); this.setLeadingIcons([icon]); }
diff --git a/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp b/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp index 48d0c4a..152ba10 100644 --- a/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp +++ b/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp
@@ -27,15 +27,15 @@ <message name="IDS_DEVTOOLS_15fa5e18dc110ac96ef35fd19abc78b3" desc=""> Time Cached </message> - <message name="IDS_DEVTOOLS_1722d5b7f86af20cbd8d1981f23a15a3" desc=""> - cookies used by frames from - </message> <message name="IDS_DEVTOOLS_1818d506396d77b3d035f719885c4cd1" desc=""> focus </message> <message name="IDS_DEVTOOLS_1bea0f12b50db07ee7f2265b790417ff" desc=""> Installability </message> + <message name="IDS_DEVTOOLS_1e9cf6d18aa0124e44d013131c79b0c1" desc=""> + Version: <ph name="THIS__DATABASE_VERSION">$1s</ph> (empty) + </message> <message name="IDS_DEVTOOLS_1fece652e0dde2e00d1a7662f081dd71" desc=""> The "<ph name="THIS_TABLENAME">$1s</ph>" table is empty. @@ -73,6 +73,9 @@ <message name="IDS_DEVTOOLS_3a771376134eb624f3e1fdd3d92d9f4c" desc=""> Select a cache entry above to preview </message> + <message name="IDS_DEVTOOLS_3af55819bc887213086b7ba5c5e25042" desc=""> + Version: <ph name="THIS__DATABASE_VERSION">$1s</ph> + </message> <message name="IDS_DEVTOOLS_3afd748bcc6315d69cff002ec6c377ed" desc=""> <ph name="THIS__REGISTRATION_ERRORS_LENGTH">$1s</ph> registration errors </message> @@ -184,6 +187,9 @@ <message name="IDS_DEVTOOLS_8f67973007158337346584551b093be8" desc=""> Icons </message> + <message name="IDS_DEVTOOLS_9093b5a47841e196aeee81e9441eb23b" desc=""> + cookies used by frames from <ph name="COOKIEDOMAIN">$1s</ph> + </message> <message name="IDS_DEVTOOLS_90e4c7584668933aebe8cf1cbccfe82d" desc=""> Please confirm delete of "<ph name="THIS__DATABASE_DATABASEID_NAME">$1s</ph>" database. </message> @@ -206,9 +212,6 @@ <message name="IDS_DEVTOOLS_9b2a7456cec10d8b5ab8ce656598320b" desc=""> Key path: </message> - <message name="IDS_DEVTOOLS_9b790a1c94937c437f9801d3c970efa7" desc=""> - (empty) - </message> <message name="IDS_DEVTOOLS_9c6a9d9f033001a9b3104984d319563b" desc=""> Push </message> @@ -344,6 +347,9 @@ <message name="IDS_DEVTOOLS_da131b6bd53f501c1323af4b198740d2" desc=""> Push Messaging </message> + <message name="IDS_DEVTOOLS_db01fae7397127fd9712b2efe6f8346f" desc=""> + Key path: <ph name="KEYPATHSTRING">$1s</ph> + </message> <message name="IDS_DEVTOOLS_dc105ac52c237f9f09c74b0767f64f74" desc=""> Content-Type </message>
diff --git a/third_party/blink/renderer/devtools/front_end/settings/FrameworkBlackboxSettingsTab.js b/third_party/blink/renderer/devtools/front_end/settings/FrameworkBlackboxSettingsTab.js index eee7f01..c6571053 100644 --- a/third_party/blink/renderer/devtools/front_end/settings/FrameworkBlackboxSettingsTab.js +++ b/third_party/blink/renderer/devtools/front_end/settings/FrameworkBlackboxSettingsTab.js
@@ -73,8 +73,7 @@ const element = createElementWithClass('div', 'blackbox-list-item'); const pattern = element.createChild('div', 'blackbox-pattern'); pattern.textContent = item.pattern; - pattern.title = ls`Blackbox scripts whose names match` + - ' \'' + item.pattern + '\''; + pattern.title = ls`Blackbox scripts whose names match '${item.pattern}'`; element.createChild('div', 'blackbox-separator'); element.createChild('div', 'blackbox-behavior').textContent = item.disabled ? this._disabledLabel : this._blackboxLabel;
diff --git a/third_party/blink/renderer/devtools/front_end/settings/settings_strings.grdp b/third_party/blink/renderer/devtools/front_end/settings/settings_strings.grdp index 594f0c9..bfaeef6 100644 --- a/third_party/blink/renderer/devtools/front_end/settings/settings_strings.grdp +++ b/third_party/blink/renderer/devtools/front_end/settings/settings_strings.grdp
@@ -15,6 +15,9 @@ <message name="IDS_DEVTOOLS_4829262cecb9828817b33e0f9c907f91" desc=""> Experiments </message> + <message name="IDS_DEVTOOLS_4a3aaf1edf15288e9ff3994088e2277a" desc=""> + Blackbox scripts whose names match '<ph name="ITEM_PATTERN">$1s</ph>' + </message> <message name="IDS_DEVTOOLS_57391192dfa1f247ad015a0fe2eca48e" desc=""> Pattern </message> @@ -36,9 +39,6 @@ <message name="IDS_DEVTOOLS_c9deece3e6de26d07ef6409b96f21fd0" desc=""> Blackboxing </message> - <message name="IDS_DEVTOOLS_ce85958ff51e825dcf797d0451b96b71" desc=""> - Blackbox scripts whose names match - </message> <message name="IDS_DEVTOOLS_d0834fcec6337785ee749c8f5464f6f6" desc=""> Preferences </message>
diff --git a/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js b/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js index 2088c4f6..d2aa44d 100644 --- a/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js +++ b/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
@@ -662,10 +662,10 @@ UI.asyncStackTraceLabel = function(description) { if (description) { if (description === 'Promise.resolve') - description = Common.UIString('Promise resolved'); + return ls`Promise resolved (async)`; else if (description === 'Promise.reject') - description = Common.UIString('Promise rejected'); - return description + ' ' + Common.UIString('(async)'); + return ls`Promise rejected (async)`; + return ls`${description} (async)`; } return Common.UIString('Async Call'); };
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp b/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp index 263028b..0088e73 100644 --- a/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp +++ b/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp
@@ -24,8 +24,8 @@ <message name="IDS_DEVTOOLS_0f8854fb5bc175b0cd33a1e8518b366f" desc=""> Switch to pan mode </message> - <message name="IDS_DEVTOOLS_10f0bad7213cafe18fb1cf55180a11da" desc=""> - (async) + <message name="IDS_DEVTOOLS_103e6e056329361a5d169154f52771d0" desc=""> + <ph name="DESCRIPTION">$1s</ph> (async) </message> <message name="IDS_DEVTOOLS_1299e7f10b9d0110e7fa5368d1307500" desc=""> long text was truncated (<ph name="TOTALBYTES">$1s</ph>) @@ -123,9 +123,6 @@ <message name="IDS_DEVTOOLS_678b7bbae3b9b67cee8bce1fac3067d7" desc=""> Full list of DevTools keyboard shortcuts and gestures </message> - <message name="IDS_DEVTOOLS_6a5fa3625d6317cd74f686720d9c411e" desc=""> - Promise rejected - </message> <message name="IDS_DEVTOOLS_6e0e121820384f135321cb766273f4da" desc=""> Soft undo </message> @@ -228,6 +225,9 @@ <message name="IDS_DEVTOOLS_b508f4cb8252d1d90689d1a61344c8fc" desc=""> <ph name="ITEM_LABEL">$1s</ph>, checked, <ph name="ITEM_SHORTCUT">$2s</ph> </message> + <message name="IDS_DEVTOOLS_b6da8d6392710f305d7176a134971514" desc=""> + Promise rejected (async) + </message> <message name="IDS_DEVTOOLS_b79165bf6163186253ed04568e1aee11" desc=""> Pause/ Continue </message> @@ -237,6 +237,9 @@ <message name="IDS_DEVTOOLS_b7ad6a024765bbfbff22dedf9afe0d17" desc=""> Jump to previous editing location </message> + <message name="IDS_DEVTOOLS_b94d1b250ba62f06dadf0edc4a947f5c" desc=""> + Promise resolved (async) + </message> <message name="IDS_DEVTOOLS_bc39d641a1e16fb73f2ce5520d250ed0" desc=""> Go to member </message> @@ -249,9 +252,6 @@ <message name="IDS_DEVTOOLS_d06337cfbcc777e0a7caa50ef1a9986a" desc=""> More tabs </message> - <message name="IDS_DEVTOOLS_d2923d7d060c61c17d06c22166b2be41" desc=""> - Promise resolved - </message> <message name="IDS_DEVTOOLS_d9f57fb1f634a4e047fd69b574434c7f" desc=""> Evaluate selection in console </message>
diff --git a/third_party/blink/renderer/devtools/scripts/check_localizability.js b/third_party/blink/renderer/devtools/scripts/check_localizability.js index 30102a70..e55d741 100644 --- a/third_party/blink/renderer/devtools/scripts/check_localizability.js +++ b/third_party/blink/renderer/devtools/scripts/check_localizability.js
@@ -88,15 +88,30 @@ /** * Recursively check if there is concatenation to localization call. - * Concatenation is allowed between localized strings and non-alphabetic strings. - * It is not allowed between a localized string and a word. - * Example (allowed): ls`Status Code` + ": " - * Example (disallowed): ls`Status` + " Code" + ": " + * Concatenation is allowed between localized strings and strings that + * don't contain letters. + * Example (allowed): ls`Status code: ${statusCode}` + * Example (allowed): ls`Status code` + ': ' + * Example (disallowed): ls`Status code: ` + statusCode + * Example (disallowed): ls`Status ` + 'code' */ function checkConcatenation(parentNode, node, filePath, errors) { - function isWord(node) { - return (node.type === esprimaTypes.LITERAL && !!node.value.match(/[a-z]/i)); + function isConcatenationDisallowed(node) { + if (node.type !== esprimaTypes.LITERAL && node.type !== esprimaTypes.TEMP_LITERAL) + return true; + + let value; + if (node.type === esprimaTypes.LITERAL) + value = node.value; + else if (node.type === esprimaTypes.TEMP_LITERAL && node.expressions.length === 0) + value = node.quasis[0].value.cooked; + + if (!value || typeof value !== 'string') + return true; + + return value.match(/[a-z]/i) !== null; } + function isConcatenation(node) { return (node !== undefined && node.type === esprimaTypes.BI_EXPR && node.operator === '+'); } @@ -107,11 +122,12 @@ if (isConcatenation(node)) { const concatenatedNodes = []; buildConcatenatedNodesList(node, concatenatedNodes); - const hasLocalizationCall = - !!concatenatedNodes.find(currentNode => localizationUtils.isLocalizationCall(currentNode)); + const nonLocalizationCalls = concatenatedNodes.filter(node => !localizationUtils.isLocalizationCall(node)); + const hasLocalizationCall = nonLocalizationCalls.length !== concatenatedNodes.length; if (hasLocalizationCall) { - const hasAlphabeticLiteral = !!concatenatedNodes.find(currentNode => isWord(currentNode)); - if (hasAlphabeticLiteral) { + // concatenation with localization call + const hasConcatenationViolation = nonLocalizationCalls.some(isConcatenationDisallowed); + if (hasConcatenationViolation) { const code = escodegen.generate(node); addError( `${filePath}${
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc index 1b93b8db..31460709 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1399,12 +1399,12 @@ } AXObject* AXNodeObject::InPageLinkTarget() const { - if (!node_ || !IsHTMLAnchorElement(node_) || !GetDocument()) + if (!IsAnchor() || !GetDocument()) return AXObject::InPageLinkTarget(); - HTMLAnchorElement* anchor = ToHTMLAnchorElement(node_.Get()); + Element* anchor = AnchorElement(); DCHECK(anchor); - KURL link_url = anchor->Href(); + KURL link_url = anchor->HrefURL(); if (!link_url.IsValid()) return AXObject::InPageLinkTarget(); String fragment = link_url.FragmentIdentifier(); @@ -1777,7 +1777,12 @@ } KURL AXNodeObject::Url() const { - if (IsAnchor() && IsHTMLAnchorElement(GetNode())) { + if (IsAnchor()) { + // Some non-HTML elements, most notably SVG <a> elements, can act as + // links/anchors. + if (!IsHTMLAnchorElement(GetNode())) + return AnchorElement()->HrefURL(); + if (HTMLAnchorElement* anchor = ToHTMLAnchorElementOrNull(AnchorElement())) return anchor->Href(); }
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc index 9f433bca..dd2de0bc 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
@@ -7,6 +7,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h" #include "third_party/blink/renderer/modules/accessibility/testing/accessibility_test.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.h" namespace blink { namespace test { @@ -165,5 +166,60 @@ EXPECT_EQ(GetAXObjectCache().InOrderTraversalBegin(), iter); } +TEST_F(AccessibilityTest, AxNodeObjectContainsHtmlAnchorElementUrl) { + SetBodyInnerHTML(R"HTML(<a id="anchor" href="http://test.com">link</a>)HTML"); + + const AXObject* root = GetAXRootObject(); + ASSERT_NE(nullptr, root); + const AXObject* anchor = GetAXObjectByElementId("anchor"); + ASSERT_NE(nullptr, anchor); + + // Passing a malformed string to KURL returns an empty URL, so verify the + // AXObject's URL is non-empty first to catch errors in the test itself. + EXPECT_FALSE(anchor->Url().IsEmpty()); + EXPECT_EQ(anchor->Url(), KURL("http://test.com")); +} + +TEST_F(AccessibilityTest, AxNodeObjectContainsSvgAnchorElementUrl) { + SetBodyInnerHTML(R"HTML( + <svg> + <a id="anchor" xlink:href="http://test.com"></a> + </svg> + )HTML"); + + const AXObject* root = GetAXRootObject(); + ASSERT_NE(nullptr, root); + const AXObject* anchor = GetAXObjectByElementId("anchor"); + ASSERT_NE(nullptr, anchor); + + EXPECT_FALSE(anchor->Url().IsEmpty()); + EXPECT_EQ(anchor->Url(), KURL("http://test.com")); +} + +TEST_F(AccessibilityTest, AxNodeObjectContainsImageUrl) { + SetBodyInnerHTML(R"HTML(<img id="anchor" src="http://test.png" />)HTML"); + + const AXObject* root = GetAXRootObject(); + ASSERT_NE(nullptr, root); + const AXObject* anchor = GetAXObjectByElementId("anchor"); + ASSERT_NE(nullptr, anchor); + + EXPECT_FALSE(anchor->Url().IsEmpty()); + EXPECT_EQ(anchor->Url(), KURL("http://test.png")); +} + +TEST_F(AccessibilityTest, AxNodeObjectContainsInPageLinkTarget) { + GetDocument().SetBaseURLOverride(KURL("http://test.com")); + SetBodyInnerHTML(R"HTML(<a id="anchor" href="#target">link</a>)HTML"); + + const AXObject* root = GetAXRootObject(); + ASSERT_NE(nullptr, root); + const AXObject* anchor = GetAXObjectByElementId("anchor"); + ASSERT_NE(nullptr, anchor); + + EXPECT_FALSE(anchor->Url().IsEmpty()); + EXPECT_EQ(anchor->Url(), KURL("http://test.com/#target")); +} + } // namespace test } // namespace blink
diff --git a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.cc b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.cc index dac0583..96fa3ae 100644 --- a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.cc +++ b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.cc
@@ -81,6 +81,7 @@ } ScriptPromise ContactsManager::select(ScriptState* script_state, + const Vector<String>& properties, ContactsSelectOptions* options) { Document* document = To<Document>(ExecutionContext::From(script_state)); if (!LocalFrame::HasTransientUserActivation(document ? document->GetFrame() @@ -91,7 +92,7 @@ "A user gesture is required to call this method")); } - if (!options->hasProperties() || !options->properties().size()) { + if (properties.IsEmpty()) { return ScriptPromise::Reject(script_state, V8ThrowException::CreateTypeError( script_state->GetIsolate(), @@ -105,7 +106,7 @@ bool include_emails = false; bool include_tel = false; - for (const String& property : options->properties()) { + for (const String& property : properties) { if (property == "name") include_names = true; else if (property == "email")
diff --git a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.h b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.h index 9bc77135..9d016f1 100644 --- a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.h +++ b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.h
@@ -10,6 +10,7 @@ #include "third_party/blink/renderer/modules/contacts_picker/contacts_select_options.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/heap/thread_state.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" namespace blink { @@ -26,6 +27,7 @@ // Web-exposed function defined in the IDL file. ScriptPromise select(ScriptState* script_state, + const Vector<String>& properties, ContactsSelectOptions* options); private:
diff --git a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.idl b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.idl index 73ac590..a208a78 100644 --- a/third_party/blink/renderer/modules/contacts_picker/contacts_manager.idl +++ b/third_party/blink/renderer/modules/contacts_picker/contacts_manager.idl
@@ -9,5 +9,5 @@ SecureContext, RuntimeEnabled=ContactsManager ] interface ContactsManager { - [CallWith=ScriptState] Promise<sequence<ContactInfo>> select(ContactsSelectOptions options); + [CallWith=ScriptState] Promise<sequence<ContactInfo>> select(sequence<ContactProperty> properties, optional ContactsSelectOptions options); };
diff --git a/third_party/blink/renderer/modules/contacts_picker/contacts_select_options.idl b/third_party/blink/renderer/modules/contacts_picker/contacts_select_options.idl index 9acfe325..4cab707d 100644 --- a/third_party/blink/renderer/modules/contacts_picker/contacts_select_options.idl +++ b/third_party/blink/renderer/modules/contacts_picker/contacts_select_options.idl
@@ -7,6 +7,5 @@ enum ContactProperty { "email", "name", "tel" }; dictionary ContactsSelectOptions { - sequence<ContactProperty> properties; boolean multiple = false; };
diff --git a/third_party/blink/renderer/modules/contacts_picker/navigator_contacts.idl b/third_party/blink/renderer/modules/contacts_picker/navigator_contacts.idl index bb47e9c..65bed25 100644 --- a/third_party/blink/renderer/modules/contacts_picker/navigator_contacts.idl +++ b/third_party/blink/renderer/modules/contacts_picker/navigator_contacts.idl
@@ -8,5 +8,5 @@ Exposed=Window, ImplementedAs=NavigatorContacts ] partial interface Navigator { - [SecureContext, RuntimeEnabled=ContactsManager] readonly attribute ContactsManager contacts; + [SecureContext, SameObject, RuntimeEnabled=ContactsManager] readonly attribute ContactsManager contacts; };
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc index ece3db0b..560f790 100644 --- a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc +++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
@@ -292,7 +292,8 @@ "this device unless the device is secured " "with a screen lock."); case CredentialManagerError::ABORT: - return MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError); + return MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError, + "Request has been aborted."); case CredentialManagerError::UNKNOWN: return MakeGarbageCollected<DOMException>( DOMExceptionCode::kNotReadableError, @@ -523,8 +524,8 @@ if (options->hasSignal()) { if (options->signal()->aborted()) { - resolver->Reject( - MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError)); + resolver->Reject(MakeGarbageCollected<DOMException>( + DOMExceptionCode::kAbortError, "Request has been aborted.")); return promise; } options->signal()->AddAlgorithm( @@ -705,8 +706,8 @@ if (options->hasSignal()) { if (options->signal()->aborted()) { - resolver->Reject( - MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError)); + resolver->Reject(MakeGarbageCollected<DOMException>( + DOMExceptionCode::kAbortError, "Request has been aborted.")); return promise; } options->signal()->AddAlgorithm(
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc index 70a1b7a..2550ad91 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
@@ -145,16 +145,46 @@ Supplement<LocalDOMWindow>::Trace(visitor); } +void PaintWorklet::RegisterCSSPaintDefinition(const String& name, + CSSPaintDefinition* definition, + ExceptionState& exception_state) { + if (document_definition_map_.Contains(name)) { + DocumentPaintDefinition* existing_document_definition = + document_definition_map_.at(name); + if (existing_document_definition == kInvalidDocumentPaintDefinition) + return; + if (!existing_document_definition->RegisterAdditionalPaintDefinition( + *definition)) { + document_definition_map_.Set(name, kInvalidDocumentPaintDefinition); + exception_state.ThrowDOMException( + DOMExceptionCode::kNotSupportedError, + "A class with name:'" + name + + "' was registered with a different definition."); + return; + } + // Notify the generator ready only when register paint is called the + // second time with the same |name| (i.e. there is already a document + // definition associated with |name| + if (existing_document_definition->GetRegisteredDefinitionCount() == + PaintWorklet::kNumGlobalScopes) + pending_generator_registry_->NotifyGeneratorReady(name); + } else { + DocumentPaintDefinition* document_definition = + MakeGarbageCollected<DocumentPaintDefinition>(definition); + document_definition_map_.Set(name, document_definition); + } +} + void PaintWorklet::RegisterMainThreadDocumentPaintDefinition( const String& name, Vector<CSSPropertyID> native_properties, Vector<String> custom_properties, - std::unique_ptr<Vector<CSSSyntaxDescriptor>> input_argument_types, + Vector<CSSSyntaxDescriptor> input_argument_types, double alpha) { DCHECK(!main_thread_document_definition_map_.Contains(name)); auto definition = std::make_unique<MainThreadDocumentPaintDefinition>( std::move(native_properties), std::move(custom_properties), - std::move(*input_argument_types), alpha); + std::move(input_argument_types), alpha); main_thread_document_definition_map_.insert(name, std::move(definition)); pending_generator_registry_->NotifyGeneratorReady(name); }
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.h b/third_party/blink/renderer/modules/csspaint/paint_worklet.h index 3951f907..cbf1084b 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet.h +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.h
@@ -60,6 +60,10 @@ return document_definition_map_; } + void RegisterCSSPaintDefinition(const String& name, + CSSPaintDefinition*, + ExceptionState&); + // Used for off-thread CSS Paint. In this mode we are not responsible for // tracking whether a definition is valid - this method should only be called // once all global scopes have registered the same @@ -68,7 +72,7 @@ const String& name, Vector<CSSPropertyID> native_properties, Vector<String> custom_properties, - std::unique_ptr<Vector<CSSSyntaxDescriptor>> input_argument_types, + Vector<CSSSyntaxDescriptor> input_argument_types, double alpha); typedef HashMap<String, std::unique_ptr<MainThreadDocumentPaintDefinition>> MainThreadDocumentDefinitionMap;
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc index 347872f..9ab00290 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
@@ -226,33 +226,8 @@ } else { PaintWorklet* paint_worklet = PaintWorklet::From(*GetFrame()->GetDocument()->domWindow()); - PaintWorklet::DocumentDefinitionMap& document_definition_map = - paint_worklet->GetDocumentDefinitionMap(); - if (document_definition_map.Contains(name)) { - DocumentPaintDefinition* existing_document_definition = - document_definition_map.at(name); - if (existing_document_definition == kInvalidDocumentPaintDefinition) - return; - if (!existing_document_definition->RegisterAdditionalPaintDefinition( - *definition)) { - document_definition_map.Set(name, kInvalidDocumentPaintDefinition); - exception_state.ThrowDOMException( - DOMExceptionCode::kNotSupportedError, - "A class with name:'" + name + - "' was registered with a different definition."); - return; - } - // Notify the generator ready only when register paint is called the - // second time with the same |name| (i.e. there is already a document - // definition associated with |name| - if (existing_document_definition->GetRegisteredDefinitionCount() == - PaintWorklet::kNumGlobalScopes) - pending_generator_registry_->NotifyGeneratorReady(name); - } else { - DocumentPaintDefinition* document_definition = - MakeGarbageCollected<DocumentPaintDefinition>(definition); - document_definition_map.Set(name, document_definition); - } + paint_worklet->RegisterCSSPaintDefinition(name, definition, + exception_state); } }
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc index 0a79961..4967e99 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
@@ -112,17 +112,13 @@ for (const auto& property : custom_properties) passed_custom_properties.push_back(property.GetString()); - Vector<CSSSyntaxDescriptor> passed_input_argument_types; - for (const auto& syntax_descriptor : definition->InputArgumentTypes()) - passed_input_argument_types.push_back(syntax_descriptor.IsolatedCopy()); PostCrossThreadTask( *main_thread_runner_, FROM_HERE, CrossThreadBindOnce( &PaintWorklet::RegisterMainThreadDocumentPaintDefinition, paint_worklet_, name, definition->NativeInvalidationProperties(), WTF::Passed(std::move(passed_custom_properties)), - std::make_unique<Vector<CSSSyntaxDescriptor>>( - passed_input_argument_types), + definition->InputArgumentTypes(), definition->GetPaintRenderingContext2DSettings()->alpha())); } }
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc index 491cc5b..ed5cb1c 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
@@ -190,7 +190,7 @@ TestPaintWorklet* paint_worklet_to_test = GetTestPaintWorklet(); paint_worklet_to_test->RegisterMainThreadDocumentPaintDefinition( "foo", native_invalidation_properties, custom_invalidation_properties, - std::make_unique<Vector<CSSSyntaxDescriptor>>(), true); + Vector<CSSSyntaxDescriptor>(), true); CSSPaintImageGeneratorImpl* generator = MakeGarbageCollected<CSSPaintImageGeneratorImpl>(paint_worklet_to_test,
diff --git a/third_party/blink/renderer/modules/wake_lock/DEPS b/third_party/blink/renderer/modules/wake_lock/DEPS index 665d622e..2cfae9a7 100644 --- a/third_party/blink/renderer/modules/wake_lock/DEPS +++ b/third_party/blink/renderer/modules/wake_lock/DEPS
@@ -3,7 +3,6 @@ "+services/device/public/mojom", "+services/device/public/interfaces/constants.mojom-blink.h", "+services/device/public/mojom/wake_lock.mojom-blink.h", - "+third_party/blink/renderer/modules/event_target_modules.h", ] specific_include_rules = {
diff --git a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc index a429a23..c060e4a 100644 --- a/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc +++ b/third_party/blink/renderer/modules/wake_lock/wake_lock_state_record.cc
@@ -66,7 +66,7 @@ // 2. Reject lockPromise with an "AbortError" DOMException. ScriptPromiseResolver* resolver = *iterator; resolver->Reject(MakeGarbageCollected<DOMException>( - DOMExceptionCode::kAbortError, "Wake Lock is being released")); + DOMExceptionCode::kAbortError, "Wake Lock released")); // 3. Let document be the responsible document of the current settings object. // 4. Let record be the platform wake lock's state record associated with
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index dfd7b1d9f..99c5670 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -294,6 +294,25 @@ crbug.com/836884 virtual/disable-blink-gen-property-trees/compositing/overflow/composited-scroll-with-fractional-translation.html [ Failure ] crbug.com/836884 virtual/disable-blink-gen-property-trees/compositing/video/video-controls-layer-creation-squashing.html [ Failure ] +# These fail when device_scale_factor is changed, but only for anti-aliasing: +crbug.com/968791 virtual/scalefactor200/css3/filters/effect-blur-hw.html [ Failure ] +crbug.com/968791 virtual/scalefactor200/css3/filters/filterRegions.html [ Failure ] +crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/css-filters-animation-blur.html [ Failure ] +crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/css-filters-animation-combined-001.html [ Failure ] +crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/filter-subregion-01.html [ Failure ] + +# These appear to be actually incorrect at device_scale_factor 2.0: +crbug.com/968791 virtual/scalefactor200/css3/filters/effect-reference-image-hw.html [ Failure ] +crbug.com/968791 virtual/scalefactor200/css3/filters/effect-reference-image-lazy-attach.html [ Failure ] +crbug.com/968791 virtual/scalefactor200/css3/filters/effect-reference-image.html [ Failure ] +crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/filtered-inline-applies-to-float.html [ Failure ] +crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/filters-test-brightness-003.html [ Failure ] + +# These seem flaky on scalefactor200: +crbug.com/968791 virtual/scalefactor200/css3/filters/effect-reference-hw.html [ Pass Failure Timeout ] +crbug.com/968791 virtual/scalefactor200/css3/filters/effect-reference-subregion-hw.html [ Pass Failure Timeout ] +crbug.com/968791 virtual/scalefactor200/css3/filters/effect-contrast-hw.html [ Pass Failure Timeout ] + # ==== Regressions introduced by BlinkGenPropertyTrees ===== # Reflection / mask ordering issue crbug.com/767318 compositing/reflections/nested-reflection-mask-change.html [ Failure ] @@ -1659,6 +1678,7 @@ crbug.com/538697 [ Win ] printing/webgl-oversized-printing.html [ Failure Crash ] crbug.com/904592 css3/filters/backdrop-filter-svg.html [ Failure ] +crbug.com/904592 virtual/scalefactor200/css3/filters/backdrop-filter-svg.html [ Failure ] crbug.com/492664 [ Linux ] external/wpt/css/css-writing-modes/box-offsets-rel-pos-vlr-005.xht [ Failure ] crbug.com/492664 [ Linux ] external/wpt/css/css-writing-modes/box-offsets-rel-pos-vrl-004.xht [ Failure ] @@ -5845,3 +5865,6 @@ # Sheriff 2019-06-04 crbug.com/970135 [ Mac ] virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-interested-element-indicated.html [ Failure ] +crbug.com/970334 [ Mac ] fast/spatial-navigation/snav-tiny-table-traversal.html [ Failure ] +crbug.com/970142 http/tests/security/mixedContent/insecure-css-resources.html [ Failure ] +crbug.com/970142 virtual/blink-cors/http/tests/security/mixedContent/insecure-css-resources.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites index a1e6ca1..a8c2f87 100644 --- a/third_party/blink/web_tests/VirtualTestSuites +++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -328,6 +328,16 @@ "args": ["--force-device-scale-factor=2"] }, { + "prefix": "scalefactor200", + "base": "css3/filters", + "args": ["--force-device-scale-factor=2"] + }, + { + "prefix": "scalefactor200", + "base": "external/wpt/css/filter-effects", + "args": ["--force-device-scale-factor=2"] + }, + { "prefix": "scalefactor150", "base": "fast/hidpi/static", "args": ["--force-device-scale-factor=1.5"]
diff --git a/third_party/blink/web_tests/WebDriverExpectations b/third_party/blink/web_tests/WebDriverExpectations index 528727a..9acec1f 100644 --- a/third_party/blink/web_tests/WebDriverExpectations +++ b/third_party/blink/web_tests/WebDriverExpectations
@@ -52,6 +52,7 @@ crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/user_prompts.py>>test_dismiss[capabilities0-alert] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_set_to_state[realmSetting0-denied] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/fullscreen_window/user_prompts.py>>test_dismiss[capabilities0-alert-None] [ Failure ] +crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/promise.py>>test_promise_reject_timeout [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/maximize_window/maximize.py>>test_maximize_when_resized_to_max_size [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/json_serialize_windowproxy.py>>test_window_open [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_set_to_state_cross_realm[realmSetting0-denied] [ Failure ] @@ -60,6 +61,7 @@ crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/user_prompts.py>>test_default[prompt] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_set_to_state[realmSetting0-prompt] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_screenshot/user_prompts.py>>test_ignore[capabilities0-alert] [ Failure ] +crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/promise.py>>test_promise_resolve_timeout [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_screenshot/user_prompts.py>>test_dismiss_and_notify[capabilities0-prompt] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/perform_actions/key_events.py>>test_modifier_key_sends_correct_events[\ue03d-META] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/user_prompts.py>>test_dismiss_and_notify[capabilities0-prompt] [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-letter-crash.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-letter-crash.html new file mode 100644 index 0000000..683d2a7 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-letter-crash.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +<link rel="help" href="https://crbug.com/967194"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +#target:first-letter{ + float:right; +} +</style> +The test passes if it does not CRASH. +<div id="target"> + <textarea style="display: block;">A</textarea> +</div> +<script>test(() => { });</script>
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/active-processing.https.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/active-processing.https.html new file mode 100644 index 0000000..0fa3089a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/active-processing.https.html
@@ -0,0 +1,100 @@ +<!doctype html> +<html> + <head> + <title> + Test Active Processing for AudioBufferSourceNode + </title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/webaudio/resources/audit-util.js"></script> + <script src="/webaudio/resources/audit.js"></script> + </head> + + <body> + <script id="layout-test-code"> + let audit = Audit.createTaskRunner(); + + // Arbitrary sample rate. And we only new a few blocks for rendering to + // see if things are working. + let sampleRate = 8000; + let renderLength = 10 * RENDER_QUANTUM_FRAMES; + + // Offline context used for the tests. + let context; + + // Number of channels for the AudioBufferSource. Fairly arbitrary, but + // should be more than 2. + let numberOfChannels = 7; + + // Number of frames in the AudioBuffer. Fairly arbitrary, but should + // probablybe more than one render quantum and significantly less than + // |renderLength|. + let bufferFrames = 131; + + let filePath = + '../the-audioworklet-interface/processors/input-count-processor.js'; + + audit.define('Setup graph', (task, should) => { + context = + new OfflineAudioContext(numberOfChannels, renderLength, sampleRate); + + should( + context.audioWorklet.addModule(filePath).then(() => { + let buffer = new AudioBuffer({ + numberOfChannels: numberOfChannels, + length: bufferFrames, + sampleRate: context.sampleRate + }); + + src = new AudioBufferSourceNode(context, {buffer: buffer}); + let counter = new AudioWorkletNode(context, 'counter'); + + src.connect(counter).connect(context.destination); + src.start(); + }), + 'AudioWorklet and graph construction') + .beResolved() + .then(() => task.done()); + }); + + audit.define('verify count change', (task, should) => { + context.startRendering() + .then(renderedBuffer => { + let output = renderedBuffer.getChannelData(0); + + // Find the first time the number of channels changes to 1. + let countChangeIndex = output.findIndex(x => x == 1); + + // Verify that the count did change. If it didn't there's a bug + // in the imploementation, or it takes longer than the render + // length to change. for the latter case, increase the render + // length, but it can't be arbitrarily large. The change needs to + // happen at some reasonable time after the source stops. + should(countChangeIndex >= 0, 'Number of channels changed') + .beTrue(); + should( + countChangeIndex, 'Index where input channel count changed') + .beLessThanOrEqualTo(renderLength); + + // Verify the number of channels at the beginning matches the + // number of channels in the AudioBuffer. + should( + output.slice(0, countChangeIndex), + `Number of channels in input[0:${countChangeIndex - 1}]`) + .beConstantValueOf(numberOfChannels); + + // Verify that after the source has stopped, the number of + // channels is 1. + should( + output.slice(countChangeIndex), + `Number of channels in input[${countChangeIndex}:]`) + .beConstantValueOf(1); + }) + .then(() => task.done()); + }); + + audit.run(); + </script> + + </body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/processors/input-count-processor.js b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/processors/input-count-processor.js new file mode 100644 index 0000000..6d53ba8 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/processors/input-count-processor.js
@@ -0,0 +1,22 @@ +/** + * @class CountProcessor + * @extends AudioWorkletProcessor + * + * This processor class just looks at the number of input channels on the first + * input and fills the first output channel with that value. + */ +class CountProcessor extends AudioWorkletProcessor { + constructor() { + super(); + } + + process(inputs, outputs, parameters) { + let input = inputs[0]; + let output = outputs[0]; + output[0].fill(input.length); + + return true; + } +} + +registerProcessor('counter', CountProcessor);
diff --git a/third_party/blink/web_tests/http/tests/contacts/resources/non-main-frame-select.html b/third_party/blink/web_tests/http/tests/contacts/resources/non-main-frame-select.html index c75186909..094256d 100644 --- a/third_party/blink/web_tests/http/tests/contacts/resources/non-main-frame-select.html +++ b/third_party/blink/web_tests/http/tests/contacts/resources/non-main-frame-select.html
@@ -2,13 +2,8 @@ 'use strict'; window.onload = function() { - navigator.contacts.select({ - multiple: "true", - properties: ['name', 'email'] - }).then(results => { - parent.postMessage({ errorMsg: '' }, '*'); - }).catch(exception => { - parent.postMessage({ errorMsg: exception.toString() }, '*'); - }); + navigator.contacts.select(['name', 'email'], { multiple: true }) + .then(results => parent.postMessage({ errorMsg: '' }, '*')) + .catch(exception => parent.postMessage({ errorMsg: exception.toString() }, '*')); } </script>
diff --git a/third_party/blink/web_tests/http/tests/contacts/select-function.html b/third_party/blink/web_tests/http/tests/contacts/select-function.html index 8a85615..1f1a47043 100644 --- a/third_party/blink/web_tests/http/tests/contacts/select-function.html +++ b/third_party/blink/web_tests/http/tests/contacts/select-function.html
@@ -35,7 +35,7 @@ promise_test(async () => { await expectTypeError(() => - navigator.contacts.select({ properties: ['name'] })); + navigator.contacts.select(['name'])); }, 'The Contact API requires a user gesture') @@ -44,17 +44,17 @@ // At least one property must be provided. await expectTypeError(() => navigator.contacts.select()); - await expectTypeError(() => navigator.contacts.select({ properties: [] })); + await expectTypeError(() => navigator.contacts.select([])); // Per WebIDL parsing, no invalid values may be provided. await expectTypeError(() => - navigator.contacts.select({ properties: [''] })); + navigator.contacts.select([''])); await expectTypeError(() => - navigator.contacts.select({ properties: ['foo'] })); + navigator.contacts.select(['foo'])); await expectTypeError(() => - navigator.contacts.select({ properties: ['name', 'photo'] })); + navigator.contacts.select(['name', 'photo'])); -}, 'The Contact API requires at least one valid property to be provided'); +}, 'The Contact API requires valid properties to be provided'); promise_test(async () => { triggerUserGesture(); @@ -64,8 +64,7 @@ return { contacts: null }; }); - await expectTypeError(() => - navigator.contacts.select({ properties: ['name'] })); + await expectTypeError(() => navigator.contacts.select(['name'])); }, 'The Contact API can fail when the selector cannot be opened'); @@ -83,10 +82,7 @@ }); await expectTypeError(() => { - return navigator.contacts.select({ - properties: ['name', 'email'], - multiple: true - }); + return navigator.contacts.select(['name', 'email'], { multiple: true }); }); assert_not_equals(storedOptions, null); @@ -110,10 +106,7 @@ }; }); - const results = await navigator.contacts.select({ - properties: ['name', 'email', 'tel'], - multiple: true - }); + const results = await navigator.contacts.select(['name', 'email', 'tel'], { multiple: true }); assert_equals(results.length, 2); @@ -153,9 +146,7 @@ }; }); - const results = await navigator.contacts.select({ - properties: ['name'] - }); + const results = await navigator.contacts.select(['name']); assert_equals(results.length, 1);
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png new file mode 100644 index 0000000..3f18468f --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt new file mode 100644 index 0000000..5382648 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@ + + + + +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutImage IMG id='grayscale-box'", + "position": [36, 36], + "bounds": [320, 180] + }, + { + "name": "LayoutImage IMG id='saturate-box'", + "position": [36, 264], + "bounds": [320, 180] + }, + { + "name": "LayoutImage IMG id='invert-box'", + "position": [36, 492], + "bounds": [320, 180] + }, + { + "name": "LayoutImage IMG id='brightness-box'", + "position": [36, 720], + "bounds": [320, 180] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt new file mode 100644 index 0000000..9c7b30f --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [200, 200, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1570, 1170], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1712, 1422], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [200, 200, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt new file mode 100644 index 0000000..b6d88a18 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1570, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1570, 1212], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [200, 200], + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt new file mode 100644 index 0000000..b6d88a18 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1570, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1570, 1212], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [200, 200], + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png new file mode 100644 index 0000000..623c41f --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png new file mode 100644 index 0000000..fee4115 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-expected.png new file mode 100644 index 0000000..60c2121 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png new file mode 100644 index 0000000..a70067f4 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-expected.png new file mode 100644 index 0000000..487badf5 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png new file mode 100644 index 0000000..86f4a55 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-expected.png new file mode 100644 index 0000000..a54bec4 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png new file mode 100644 index 0000000..4c9204b --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png new file mode 100644 index 0000000..3ee4f62 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png new file mode 100644 index 0000000..0e5bc0f3 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png new file mode 100644 index 0000000..007fec52 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png new file mode 100644 index 0000000..dc130c700 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png new file mode 100644 index 0000000..5d44c7f3 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png new file mode 100644 index 0000000..9980dd3 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-expected.png new file mode 100644 index 0000000..a8a07a2 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png new file mode 100644 index 0000000..a8a07a2 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-expected.png new file mode 100644 index 0000000..a2b3e735 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png new file mode 100644 index 0000000..a2b3e735 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png new file mode 100644 index 0000000..2efc3e4 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png new file mode 100644 index 0000000..393138e --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png new file mode 100644 index 0000000..bdc645b4 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png new file mode 100644 index 0000000..9f33297 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-expected.png new file mode 100644 index 0000000..ee72c0a --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png new file mode 100644 index 0000000..0065caa8 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png new file mode 100644 index 0000000..7e63a2b --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png new file mode 100644 index 0000000..df68f6bb2 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png new file mode 100644 index 0000000..eb0f3a3 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png new file mode 100644 index 0000000..6f153ab7 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-saturate-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-saturate-expected.png new file mode 100644 index 0000000..c264fe5b --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-saturate-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png new file mode 100644 index 0000000..0b4d6195 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-sepia-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-sepia-expected.png new file mode 100644 index 0000000..66cb7b23 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-sepia-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png new file mode 100644 index 0000000..72ae4a00 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png new file mode 100644 index 0000000..1538bd2a --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png new file mode 100644 index 0000000..1a60235 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/http/tests/security/cookies/basic-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/http/tests/security/cookies/basic-expected.txt new file mode 100644 index 0000000..2de19173 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/http/tests/security/cookies/basic-expected.txt
@@ -0,0 +1,4 @@ +CONSOLE WARNING: [Deprecation] A cookie associated with a cross-site resource at http://localhost:8000/security/cookies/resources/set-a-cookie.html was set without the `SameSite` attribute. Starting in M77, Chrome will only deliver cookies with cross-site requests if they are set with `SameSite=None`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592. +Running test. +secret=PASS +Test complete.
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png new file mode 100644 index 0000000..3f18468f --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png new file mode 100644 index 0000000..3b4a8ecb --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png new file mode 100644 index 0000000..79f4913 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt new file mode 100644 index 0000000..a65eaa8 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@ + + + + +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutImage IMG id='grayscale-box'", + "position": [18, 18], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='saturate-box'", + "position": [18, 132], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='invert-box'", + "position": [18, 246], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='brightness-box'", + "position": [18, 360], + "bounds": [160, 90] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt new file mode 100644 index 0000000..d9fa6bcd --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,40 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt new file mode 100644 index 0000000..700335c --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,40 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt new file mode 100644 index 0000000..40d10fc5 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 585], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [835, 711], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt new file mode 100644 index 0000000..ddac1152 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [785, 606], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [100, 100], + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt new file mode 100644 index 0000000..ddac1152 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [785, 606], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [100, 100], + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png new file mode 100644 index 0000000..46b61b2c --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png new file mode 100644 index 0000000..32ed34b --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png new file mode 100644 index 0000000..54d7015 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-expected.png new file mode 100644 index 0000000..c2c56ea6 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png new file mode 100644 index 0000000..7c6d7e50 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png new file mode 100644 index 0000000..d4fd81c1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png new file mode 100644 index 0000000..d4fd81c1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png new file mode 100644 index 0000000..bb90499 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png new file mode 100644 index 0000000..10d9bca4e --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png new file mode 100644 index 0000000..5853476 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png new file mode 100644 index 0000000..ec40383 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt new file mode 100644 index 0000000..8b4ed3f --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,55 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV class='filtered box'", + "position": [8, 8], + "bounds": [144, 144] + }, + { + "name": "LayoutBlockFlow DIV class='compositing box'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [30, 30, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 1, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt new file mode 100644 index 0000000..58ba65a --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,22 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/http/tests/security/cookies/basic-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/http/tests/security/cookies/basic-expected.txt new file mode 100644 index 0000000..b38b7e4 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/http/tests/security/cookies/basic-expected.txt
@@ -0,0 +1,3 @@ +Running test. +secret=PASS +Test complete.
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png new file mode 100644 index 0000000..3f18468f --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png new file mode 100644 index 0000000..3b4a8ecb --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png new file mode 100644 index 0000000..79f4913 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt new file mode 100644 index 0000000..a65eaa8 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@ + + + + +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutImage IMG id='grayscale-box'", + "position": [18, 18], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='saturate-box'", + "position": [18, 132], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='invert-box'", + "position": [18, 246], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='brightness-box'", + "position": [18, 360], + "bounds": [160, 90] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt new file mode 100644 index 0000000..d9fa6bcd --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,40 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt new file mode 100644 index 0000000..700335c --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,40 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt new file mode 100644 index 0000000..40d10fc5 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 585], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [835, 711], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt new file mode 100644 index 0000000..ddac1152 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [785, 606], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [100, 100], + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt new file mode 100644 index 0000000..ddac1152 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [785, 606], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [100, 100], + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png new file mode 100644 index 0000000..46b61b2c --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png new file mode 100644 index 0000000..32ed34b --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png new file mode 100644 index 0000000..54d7015 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-expected.png new file mode 100644 index 0000000..c2c56ea6 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png new file mode 100644 index 0000000..7c6d7e50 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png new file mode 100644 index 0000000..0e4a982 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png new file mode 100644 index 0000000..d4fd81c1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png new file mode 100644 index 0000000..d4fd81c1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png new file mode 100644 index 0000000..bb90499 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png new file mode 100644 index 0000000..10d9bca4e --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png new file mode 100644 index 0000000..5853476 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png new file mode 100644 index 0000000..ec40383 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt new file mode 100644 index 0000000..8b4ed3f --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,55 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV class='filtered box'", + "position": [8, 8], + "bounds": [144, 144] + }, + { + "name": "LayoutBlockFlow DIV class='compositing box'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [30, 30, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 1, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt new file mode 100644 index 0000000..58ba65a --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,22 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png new file mode 100644 index 0000000..3f18468f --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png new file mode 100644 index 0000000..3b4a8ecb --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png new file mode 100644 index 0000000..79f4913 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt new file mode 100644 index 0000000..a65eaa8 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@ + + + + +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutImage IMG id='grayscale-box'", + "position": [18, 18], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='saturate-box'", + "position": [18, 132], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='invert-box'", + "position": [18, 246], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='brightness-box'", + "position": [18, 360], + "bounds": [160, 90] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt new file mode 100644 index 0000000..d9fa6bcd --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,40 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt new file mode 100644 index 0000000..700335c --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,40 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt new file mode 100644 index 0000000..40d10fc5 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 585], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [835, 711], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt new file mode 100644 index 0000000..ddac1152 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [785, 606], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [100, 100], + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt new file mode 100644 index 0000000..ddac1152 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [785, 606], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [100, 100], + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png new file mode 100644 index 0000000..46b61b2c --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png new file mode 100644 index 0000000..32ed34b --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png new file mode 100644 index 0000000..54d7015 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-expected.png new file mode 100644 index 0000000..c2c56ea6 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png new file mode 100644 index 0000000..d4fd81c1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png new file mode 100644 index 0000000..d4fd81c1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png new file mode 100644 index 0000000..bb90499 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png new file mode 100644 index 0000000..10d9bca4e --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png new file mode 100644 index 0000000..5853476 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png new file mode 100644 index 0000000..ec40383 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt new file mode 100644 index 0000000..8b4ed3f --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,55 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV class='filtered box'", + "position": [8, 8], + "bounds": [144, 144] + }, + { + "name": "LayoutBlockFlow DIV class='compositing box'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [30, 30, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 1, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt new file mode 100644 index 0000000..58ba65a --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,22 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png new file mode 100644 index 0000000..3f18468f --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png new file mode 100644 index 0000000..3b4a8ecb --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png new file mode 100644 index 0000000..79f4913 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt new file mode 100644 index 0000000..a65eaa8 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@ + + + + +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutImage IMG id='grayscale-box'", + "position": [18, 18], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='saturate-box'", + "position": [18, 132], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='invert-box'", + "position": [18, 246], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='brightness-box'", + "position": [18, 360], + "bounds": [160, 90] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt new file mode 100644 index 0000000..d9fa6bcd --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,40 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt new file mode 100644 index 0000000..700335c --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,40 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt new file mode 100644 index 0000000..40d10fc5 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 585], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [835, 711], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt new file mode 100644 index 0000000..ddac1152 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [785, 606], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [100, 100], + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt new file mode 100644 index 0000000..ddac1152 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [785, 606], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [100, 100], + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png new file mode 100644 index 0000000..46b61b2c --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png new file mode 100644 index 0000000..32ed34b --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png new file mode 100644 index 0000000..54d7015 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-expected.png new file mode 100644 index 0000000..c2c56ea6 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png new file mode 100644 index 0000000..0e4a982 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png new file mode 100644 index 0000000..d4fd81c1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png new file mode 100644 index 0000000..d4fd81c1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png new file mode 100644 index 0000000..bb90499 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png new file mode 100644 index 0000000..10d9bca4e --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png new file mode 100644 index 0000000..5853476 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png new file mode 100644 index 0000000..ec40383 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt new file mode 100644 index 0000000..8b4ed3f --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,55 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV class='filtered box'", + "position": [8, 8], + "bounds": [144, 144] + }, + { + "name": "LayoutBlockFlow DIV class='compositing box'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [30, 30, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 1, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt new file mode 100644 index 0000000..58ba65a --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-retina/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,22 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png new file mode 100644 index 0000000..3f18468f --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png new file mode 100644 index 0000000..f0aa6cb --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png new file mode 100644 index 0000000..3b4a8ecb --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png new file mode 100644 index 0000000..79f4913 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png new file mode 100644 index 0000000..9d425786 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt new file mode 100644 index 0000000..a65eaa8 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@ + + + + +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutImage IMG id='grayscale-box'", + "position": [18, 18], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='saturate-box'", + "position": [18, 132], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='invert-box'", + "position": [18, 246], + "bounds": [160, 90] + }, + { + "name": "LayoutImage IMG id='brightness-box'", + "position": [18, 360], + "bounds": [160, 90] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt new file mode 100644 index 0000000..d9fa6bcd --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,40 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt new file mode 100644 index 0000000..700335c --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,40 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt new file mode 100644 index 0000000..40d10fc5 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 585], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [835, 711], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [100, 100, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt new file mode 100644 index 0000000..ddac1152 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [785, 606], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [100, 100], + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt new file mode 100644 index 0000000..ddac1152 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [785, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [785, 606], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [100, 100], + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [250, 250, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png new file mode 100644 index 0000000..46b61b2c --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png new file mode 100644 index 0000000..32ed34b --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png new file mode 100644 index 0000000..54d7015 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-expected.png new file mode 100644 index 0000000..c2c56ea6 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png new file mode 100644 index 0000000..16ac3e8 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.txt new file mode 100644 index 0000000..5a6ac0b --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.txt
@@ -0,0 +1,2 @@ +Content-Type: text/plain +#EOF
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-nested-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-nested-expected.png new file mode 100644 index 0000000..82e11f8 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-subregion-nested-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png new file mode 100644 index 0000000..d4fd81c1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png new file mode 100644 index 0000000..d4fd81c1 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png new file mode 100644 index 0000000..bb90499 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png new file mode 100644 index 0000000..10d9bca4e --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png new file mode 100644 index 0000000..5853476 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png new file mode 100644 index 0000000..a8cd675 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png new file mode 100644 index 0000000..1ca60cb --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png new file mode 100644 index 0000000..ec40383 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt new file mode 100644 index 0000000..8b4ed3f --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,55 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV class='filtered box'", + "position": [8, 8], + "bounds": [144, 144] + }, + { + "name": "LayoutBlockFlow DIV class='compositing box'", + "bounds": [100, 100], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [30, 30, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 1, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/nested-filter-expected.png b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/nested-filter-expected.png new file mode 100644 index 0000000..036c269 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/nested-filter-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt new file mode 100644 index 0000000..58ba65a --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,22 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png new file mode 100644 index 0000000..f76fe3d9 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt new file mode 100644 index 0000000..34f3e02e --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@ + + + + +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutImage IMG id='grayscale-box'", + "position": [36, 36], + "bounds": [320, 180] + }, + { + "name": "LayoutImage IMG id='saturate-box'", + "position": [36, 265], + "bounds": [320, 180] + }, + { + "name": "LayoutImage IMG id='invert-box'", + "position": [36, 494], + "bounds": [320, 180] + }, + { + "name": "LayoutImage IMG id='brightness-box'", + "position": [36, 723], + "bounds": [320, 180] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt new file mode 100644 index 0000000..b434669d --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [200, 200, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1570, 1170], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1712, 1376], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [200, 200, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt new file mode 100644 index 0000000..13af038d --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [200, 200], + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt new file mode 100644 index 0000000..13af038d --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [200, 200], + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png new file mode 100644 index 0000000..e026b36 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png new file mode 100644 index 0000000..2813b3e --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-expected.png new file mode 100644 index 0000000..6a8debe --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png new file mode 100644 index 0000000..0617020 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-expected.png new file mode 100644 index 0000000..c7fc81f5 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png new file mode 100644 index 0000000..81c272b --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-contrast-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-contrast-expected.png new file mode 100644 index 0000000..6d450f8a --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-contrast-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png new file mode 100644 index 0000000..9f2b1af --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png new file mode 100644 index 0000000..26aa89f --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png new file mode 100644 index 0000000..9cf56519 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png new file mode 100644 index 0000000..c61efde --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png new file mode 100644 index 0000000..1deb82bd --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png new file mode 100644 index 0000000..829abc1f5 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png new file mode 100644 index 0000000..ec8e7a77 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-invert-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-invert-expected.png new file mode 100644 index 0000000..30a1924 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-invert-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png new file mode 100644 index 0000000..30a1924 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-opacity-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-opacity-expected.png new file mode 100644 index 0000000..7395978 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-opacity-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png new file mode 100644 index 0000000..7395978 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png new file mode 100644 index 0000000..e5dd6bf --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png new file mode 100644 index 0000000..1cd8126 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png new file mode 100644 index 0000000..e7806794 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png new file mode 100644 index 0000000..ebb4d76 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-expected.png new file mode 100644 index 0000000..0fd1ee936 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png new file mode 100644 index 0000000..27727867 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png new file mode 100644 index 0000000..637bdd4 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png new file mode 100644 index 0000000..97c30c1 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-subregion-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png new file mode 100644 index 0000000..d43dc6d --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png new file mode 100644 index 0000000..0b49854f --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-saturate-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-saturate-expected.png new file mode 100644 index 0000000..002920c3 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-saturate-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png new file mode 100644 index 0000000..4711bb1 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-sepia-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-sepia-expected.png new file mode 100644 index 0000000..4b3cde0 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-sepia-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png new file mode 100644 index 0000000..61850c7 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png new file mode 100644 index 0000000..4adde80 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/filter-repaint-feimage-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png new file mode 100644 index 0000000..5de3687a --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/scalefactor200/css3/filters/filter-repaint-turbulence-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png new file mode 100644 index 0000000..f76fe3d9 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/backdrop-filter-boundary-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt new file mode 100644 index 0000000..34f3e02e --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@ + + + + +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutImage IMG id='grayscale-box'", + "position": [36, 36], + "bounds": [320, 180] + }, + { + "name": "LayoutImage IMG id='saturate-box'", + "position": [36, 265], + "bounds": [320, 180] + }, + { + "name": "LayoutImage IMG id='invert-box'", + "position": [36, 494], + "bounds": [320, 180] + }, + { + "name": "LayoutImage IMG id='brightness-box'", + "position": [36, 723], + "bounds": [320, 180] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt new file mode 100644 index 0000000..b434669d --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [200, 200, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1570, 1170], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1712, 1376], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [200, 200, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt new file mode 100644 index 0000000..13af038d --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [200, 200], + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt new file mode 100644 index 0000000..13af038d --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [200, 200], + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png new file mode 100644 index 0000000..e026b36 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png new file mode 100644 index 0000000..2813b3e --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-expected.png new file mode 100644 index 0000000..6a8debe --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png new file mode 100644 index 0000000..0617020 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-combined-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-combined-expected.png new file mode 100644 index 0000000..c7fc81f5 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-combined-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png new file mode 100644 index 0000000..81c272b --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-contrast-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-contrast-expected.png new file mode 100644 index 0000000..6d450f8a --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-contrast-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png new file mode 100644 index 0000000..9f2b1af --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png new file mode 100644 index 0000000..26aa89f --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png new file mode 100644 index 0000000..9cf56519 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png new file mode 100644 index 0000000..c61efde --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png new file mode 100644 index 0000000..829abc1f5 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png new file mode 100644 index 0000000..ec8e7a77 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png new file mode 100644 index 0000000..30a1924 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-opacity-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-opacity-expected.png new file mode 100644 index 0000000..7395978 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-opacity-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png new file mode 100644 index 0000000..e5dd6bf --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png new file mode 100644 index 0000000..1cd8126 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png new file mode 100644 index 0000000..e7806794 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png new file mode 100644 index 0000000..ebb4d76 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-expected.png new file mode 100644 index 0000000..0fd1ee936 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png new file mode 100644 index 0000000..27727867 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png new file mode 100644 index 0000000..d43dc6d --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png new file mode 100644 index 0000000..0b49854f --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png new file mode 100644 index 0000000..4711bb1 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-sepia-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-sepia-expected.png new file mode 100644 index 0000000..4b3cde0 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-sepia-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png new file mode 100644 index 0000000..61850c7 --- /dev/null +++ b/third_party/blink/web_tests/platform/win7/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/README.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/README.txt new file mode 100644 index 0000000..530ac2d --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/README.txt
@@ -0,0 +1,2 @@ +# This suite runs the tests in fast/hidpi/static with +# --force-device-scale-factor=2
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/add-filter-rendering-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/add-filter-rendering-expected.png new file mode 100644 index 0000000..4d5e090 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/add-filter-rendering-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-basic-blur-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-basic-blur-expected.png new file mode 100644 index 0000000..17a95872 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-basic-blur-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-border-radius-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-border-radius-expected.png new file mode 100644 index 0000000..f0ae23f --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-border-radius-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png new file mode 100644 index 0000000..557b0fc --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-browser-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png new file mode 100644 index 0000000..4b960016 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-clip-rect-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png new file mode 100644 index 0000000..5afe8db --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-edge-pixels-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-plus-filter-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-plus-filter-expected.png new file mode 100644 index 0000000..141896f39 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-plus-filter-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-svg-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-svg-expected.png new file mode 100644 index 0000000..66e56c8 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-svg-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png new file mode 100644 index 0000000..23174df --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/backdrop-filter-transform-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-expected.png new file mode 100644 index 0000000..b372409d --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-parents-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-parents-expected.png new file mode 100644 index 0000000..652f574 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-parents-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-self-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-self-expected.png new file mode 100644 index 0000000..adc09f2 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/blur-filter-page-scroll-self-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/buffer-offset-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/buffer-offset-expected.png new file mode 100644 index 0000000..9e969ac --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/buffer-offset-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt new file mode 100644 index 0000000..5382648 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-during-transition-layertree-expected.txt
@@ -0,0 +1,46 @@ + + + + +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutImage IMG id='grayscale-box'", + "position": [36, 36], + "bounds": [320, 180] + }, + { + "name": "LayoutImage IMG id='saturate-box'", + "position": [36, 264], + "bounds": [320, 180] + }, + { + "name": "LayoutImage IMG id='invert-box'", + "position": [36, 492], + "bounds": [320, 180] + }, + { + "name": "LayoutImage IMG id='brightness-box'", + "position": [36, 720], + "bounds": [320, 180] + } + ] +} +
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt new file mode 100644 index 0000000..b7b42ad --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-bounds-after-sw-blur-animation-expected.txt
@@ -0,0 +1,40 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer' class='final-drop-shadow'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [200, 200, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt new file mode 100644 index 0000000..6a67b2b8 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-bounds-with-composited-blur-expected.txt
@@ -0,0 +1,40 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [200, 200, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt new file mode 100644 index 0000000..9c7b30f --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change-expected.txt
@@ -0,0 +1,93 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='compositor-painted-shadow'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [200, 200, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1570, 1170], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1712, 1422], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-parent' class='software-painted-shadow'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='absolutely-positioned-composited-child'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [200, 200, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt new file mode 100644 index 0000000..b6d88a18 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-composited-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1570, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1570, 1212], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [200, 200], + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt new file mode 100644 index 0000000..b6d88a18 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-layer-promotion-after-outset-overlap-change-using-sw-shadow-expected.txt
@@ -0,0 +1,86 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1570, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1570, 1212], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='composited-layer-to-overlap'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 1 + }, + { + "name": "LayoutBlockFlow (positioned) DIV id='software-layer-to-promote' class='final-drop-shadow'", + "position": [200, 200], + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#000000" + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [500, 500, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-reflected-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-reflected-expected.png new file mode 100644 index 0000000..a926cc9 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/composited-reflected-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/crash-filter-change-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/crash-filter-change-expected.png new file mode 100644 index 0000000..c69594c --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/crash-filter-change-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/css-opacity-with-drop-shadow-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/css-opacity-with-drop-shadow-expected.png new file mode 100644 index 0000000..5cd9b2b7 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/css-opacity-with-drop-shadow-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png new file mode 100644 index 0000000..799831e --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-all-on-background-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-blur-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-blur-expected.png new file mode 100644 index 0000000..afa83a6 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-blur-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png new file mode 100644 index 0000000..4d3a707 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-blur-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png new file mode 100644 index 0000000..623c41f --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-clamping-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png new file mode 100644 index 0000000..fee4115 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-clamping-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-expected.png new file mode 100644 index 0000000..60c2121 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png new file mode 100644 index 0000000..a70067f4 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-brightness-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-combined-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-combined-expected.png new file mode 100644 index 0000000..487badf5 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-combined-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png new file mode 100644 index 0000000..86f4a55 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-combined-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-contrast-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-contrast-expected.png new file mode 100644 index 0000000..a54bec4 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-contrast-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png new file mode 100644 index 0000000..4c9204b --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-contrast-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png new file mode 100644 index 0000000..3ee4f62 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-drop-shadow-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png new file mode 100644 index 0000000..0e5bc0f3 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-drop-shadow-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png new file mode 100644 index 0000000..007fec52 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-grayscale-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png new file mode 100644 index 0000000..dc130c700 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-grayscale-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png new file mode 100644 index 0000000..5d44c7f3 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-hue-rotate-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png new file mode 100644 index 0000000..9980dd3 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-hue-rotate-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-invert-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-invert-expected.png new file mode 100644 index 0000000..a8a07a2 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-invert-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png new file mode 100644 index 0000000..a8a07a2 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-invert-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-opacity-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-opacity-expected.png new file mode 100644 index 0000000..a2b3e735 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-opacity-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png new file mode 100644 index 0000000..a2b3e735 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-opacity-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png new file mode 100644 index 0000000..2efc3e4 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-colorspace-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png new file mode 100644 index 0000000..393138e --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-colorspace-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png new file mode 100644 index 0000000..bdc645b4 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-composite-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png new file mode 100644 index 0000000..9f33297 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-composite-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-expected.png new file mode 100644 index 0000000..ee72c0a --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-external-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-external-expected.png new file mode 100644 index 0000000..1e7342c --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-external-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hidpi-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hidpi-expected.png new file mode 100644 index 0000000..3b143b7c --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hidpi-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hidpi-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hidpi-hw-expected.png new file mode 100644 index 0000000..18f04655 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hidpi-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png new file mode 100644 index 0000000..0065caa8 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-expected.png new file mode 100644 index 0000000..255d8a7 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png new file mode 100644 index 0000000..7895189 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-ordering-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-expected.png new file mode 100644 index 0000000..48d39c2 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-hw-expected.png new file mode 100644 index 0000000..806e098 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-chained-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-colormatrix-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-colormatrix-expected.png new file mode 100644 index 0000000..22c1d6e --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-colormatrix-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-nested-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-nested-expected.png new file mode 100644 index 0000000..2d3d74f --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-subregion-nested-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png new file mode 100644 index 0000000..2ec460b --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-tile-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png new file mode 100644 index 0000000..2ec460b --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-tile-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png new file mode 100644 index 0000000..eb0f3a3 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-zoom-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png new file mode 100644 index 0000000..6f153ab7 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-reference-zoom-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-saturate-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-saturate-expected.png new file mode 100644 index 0000000..c264fe5b --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-saturate-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png new file mode 100644 index 0000000..0b4d6195 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-saturate-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-sepia-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-sepia-expected.png new file mode 100644 index 0000000..66cb7b23 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-sepia-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png new file mode 100644 index 0000000..72ae4a00 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/effect-sepia-hw-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png new file mode 100644 index 0000000..fdeb602 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-change-repaint-composited-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png new file mode 100644 index 0000000..0ce12565 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-change-repaint-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-region-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-region-expected.png new file mode 100644 index 0000000..ccc9290 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-region-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-blur-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-blur-expected.png new file mode 100644 index 0000000..5dcd73a --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-blur-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png new file mode 100644 index 0000000..98eacb9 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-child-layers-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png new file mode 100644 index 0000000..659a5071 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-crash-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png new file mode 100644 index 0000000..659a5071 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-composited-fallback-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-expected.png new file mode 100644 index 0000000..a6fb29f --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-sepia-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-sepia-expected.png new file mode 100644 index 0000000..96e4dd5 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-sepia-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-clipped-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-clipped-expected.png new file mode 100644 index 0000000..15d1479 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-clipped-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-expected.png new file mode 100644 index 0000000..503fa3a3 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-rotated-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-rotated-expected.png new file mode 100644 index 0000000..f415621 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-repaint-shadow-rotated-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png new file mode 100644 index 0000000..33924a2 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filter-with-transform-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.png new file mode 100644 index 0000000..96179a8 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt new file mode 100644 index 0000000..e371a90b --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-compositing-descendant-expected.txt
@@ -0,0 +1,55 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV class='filtered box'", + "position": [16, 16], + "bounds": [288, 288] + }, + { + "name": "LayoutBlockFlow DIV class='compositing box'", + "bounds": [200, 200], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [60, 60, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 2, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-inline-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-inline-expected.png new file mode 100644 index 0000000..d3ef6ce --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/filtered-inline-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/multiple-filters-invalidation-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/multiple-filters-invalidation-expected.png new file mode 100644 index 0000000..486cbd86 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/multiple-filters-invalidation-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/nested-filter-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/nested-filter-expected.png new file mode 100644 index 0000000..9d781f08 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/nested-filter-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/nested-filters-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/nested-filters-expected.png new file mode 100644 index 0000000..6ca6e583 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/nested-filters-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/regions-expanding-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/regions-expanding-expected.png new file mode 100644 index 0000000..9ad2a23 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/regions-expanding-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/remove-filter-rendering-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/remove-filter-rendering-expected.png new file mode 100644 index 0000000..df48b71 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/remove-filter-rendering-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt new file mode 100644 index 0000000..45bbaad --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/should-not-have-compositing-layer-expected.txt
@@ -0,0 +1,22 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [1600, 1200], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [1600, 1200], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [1600, 1200], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + } + ] +} +
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/simple-filter-rendering-expected.png b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/simple-filter-rendering-expected.png new file mode 100644 index 0000000..4d5e090 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/css3/filters/simple-filter-rendering-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/external/wpt/css/filter-effects/README.txt b/third_party/blink/web_tests/virtual/scalefactor200/external/wpt/css/filter-effects/README.txt new file mode 100644 index 0000000..530ac2d --- /dev/null +++ b/third_party/blink/web_tests/virtual/scalefactor200/external/wpt/css/filter-effects/README.txt
@@ -0,0 +1,2 @@ +# This suite runs the tests in fast/hidpi/static with +# --force-device-scale-factor=2
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium index 9f58d36..1e4046d 100644 --- a/third_party/metrics_proto/README.chromium +++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@ Name: Metrics Protos Short Name: metrics_proto URL: This is the canonical public repository -Version: 249879928 -Date: 2019/05/24 UTC +Version: 250946462 +Date: 2019/05/31 UTC License: BSD Security Critical: Yes
diff --git a/third_party/metrics_proto/trace_log.proto b/third_party/metrics_proto/trace_log.proto index 9917eb7..e0247f4 100644 --- a/third_party/metrics_proto/trace_log.proto +++ b/third_party/metrics_proto/trace_log.proto
@@ -21,4 +21,53 @@ message TraceLog { // Client uploads the trace data as a byte buffer. optional bytes raw_data = 1; + + // Metadata related to the trace log, like trigger for the upload, etc. + optional TraceMetadata metadata = 3; +} + +// This field contains metadata associated with the trace. +message TraceMetadata { + // Information about a trigger rule defined in the experiment config. + message TriggerRule { + enum TriggerType { + UNKNOWN = 0; + + // Traces are triggered by specific range of values of an UMA histogram. + MONITOR_AND_DUMP_WHEN_SPECIFIC_HISTOGRAM_AND_VALUE = 1; + + // Traces are triggered by specific named events in chromium codebase, + // like "second-update-failure". + MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED = 2; + } + optional TriggerType trigger_type = 1; + + // Configuration of histogram trigger. + message HistogramRule { + // UMA histogram name hash, same as HistogramEventProto.name_hash. + optional fixed64 histogram_name_hash = 1; + + // Range of values of the histogram that activates trigger. + optional int64 histogram_min_trigger = 2; + optional int64 histogram_max_trigger = 3; + } + optional HistogramRule histogram_rule = 2; + + // Configuration of named trigger. + message NamedRule { + enum EventType { + INVALID = 0; + SESSION_RESTORE = 1; + NAVIGATION = 2; + } + optional EventType event_type = 1; + } + optional NamedRule named_rule = 3; + } + + // Specifies the rule that caused the trace to be uploaded. + optional TriggerRule triggered_rule = 1; + + // List of all active triggers in current session, when trace was triggered. + repeated TriggerRule active_rules = 2; }
diff --git a/third_party/polymer/v3_0/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js b/third_party/polymer/v3_0/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js index 93b9e37d..7d8d859 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js +++ b/third_party/polymer/v3_0/components-chromium/iron-a11y-announcer/iron-a11y-announcer.js
@@ -8,10 +8,10 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; /** `iron-a11y-announcer` is a singleton element that is intended to add a11y
diff --git a/third_party/polymer/v3_0/components-chromium/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js index dae57c4..84eb182 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; /** * Chrome uses an older version of DOM Level 3 Keyboard Events
diff --git a/third_party/polymer/v3_0/components-chromium/iron-a11y-keys/iron-a11y-keys.js b/third_party/polymer/v3_0/components-chromium/iron-a11y-keys/iron-a11y-keys.js index a84490a2..c974bb6 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-a11y-keys/iron-a11y-keys.js +++ b/third_party/polymer/v3_0/components-chromium/iron-a11y-keys/iron-a11y-keys.js
@@ -8,10 +8,10 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronA11yKeysBehavior} from '../iron-a11y-keys-behavior/iron-a11y-keys-behavior.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; /** `iron-a11y-keys` provides a cross-browser interface for processing keyboard commands. The interface adheres to [WAI-ARIA best
diff --git a/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-button-state.js b/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-button-state.js index a163674..ca9a41e7 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-button-state.js +++ b/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-button-state.js
@@ -8,11 +8,11 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import './iron-control-state.js'; import {IronA11yKeysBehavior} from '../iron-a11y-keys-behavior/iron-a11y-keys-behavior.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; /** * @demo demo/index.html
diff --git a/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-control-state.js b/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-control-state.js index 0d7d3a1e..15b4cbb8 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-control-state.js +++ b/third_party/polymer/v3_0/components-chromium/iron-behaviors/iron-control-state.js
@@ -8,9 +8,9 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; /** * @demo demo/index.html
diff --git a/third_party/polymer/v3_0/components-chromium/iron-collapse/iron-collapse.js b/third_party/polymer/v3_0/components-chromium/iron-collapse/iron-collapse.js index 75372db..f6d3af14 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-collapse/iron-collapse.js +++ b/third_party/polymer/v3_0/components-chromium/iron-collapse/iron-collapse.js
@@ -9,10 +9,10 @@ found at http://polymer.github.io/PATENTS.txt */ import {IronResizableBehavior} from '../iron-resizable-behavior/iron-resizable-behavior.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; -import {Base} from '../polymer/polymer-legacy.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; +import {Base} from '../polymer/polymer_bundled.min.js'; /** `iron-collapse` creates a collapsible block of content. By default, the content
diff --git a/third_party/polymer/v3_0/components-chromium/iron-dropdown/iron-dropdown.js b/third_party/polymer/v3_0/components-chromium/iron-dropdown/iron-dropdown.js index 288e624..83bef0e 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-dropdown/iron-dropdown.js +++ b/third_party/polymer/v3_0/components-chromium/iron-dropdown/iron-dropdown.js
@@ -8,15 +8,15 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronA11yKeysBehavior} from '../iron-a11y-keys-behavior/iron-a11y-keys-behavior.js'; import {IronControlState} from '../iron-behaviors/iron-control-state.js'; import {IronOverlayBehavior, IronOverlayBehaviorImpl} from '../iron-overlay-behavior/iron-overlay-behavior.js'; import {NeonAnimationRunnerBehavior} from '../neon-animation/neon-animation-runner-behavior.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; /** `<iron-dropdown>` is a generalized element that is useful when you have
diff --git a/third_party/polymer/v3_0/components-chromium/iron-fit-behavior/iron-fit-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-fit-behavior/iron-fit-behavior.js index 48d9b70f1..b8cae11 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-fit-behavior/iron-fit-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/iron-fit-behavior/iron-fit-behavior.js
@@ -8,9 +8,9 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; /** `Polymer.IronFitBehavior` fits an element in another element using `max-height`
diff --git a/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout-classes.js b/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout-classes.js index 54edcb4c..3e49e59 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout-classes.js +++ b/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout-classes.js
@@ -8,8 +8,8 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; /* A set of layout classes that let you specify layout properties directly in
diff --git a/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout.js b/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout.js index 279345c..a71a6135 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout.js +++ b/third_party/polymer/v3_0/components-chromium/iron-flex-layout/iron-flex-layout.js
@@ -8,8 +8,8 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; /** The `<iron-flex-layout>` component provides simple ways to use @@ -40,7 +40,7 @@ ``` ```js - import {html} from '../polymer/lib/utils/html-tag.js'; + import {html} from '../polymer/polymer_bundled.min.js'; import '../iron-flex-layout/iron-flex-layout-classes.js'; const template = html`
diff --git a/third_party/polymer/v3_0/components-chromium/iron-icon/iron-icon.js b/third_party/polymer/v3_0/components-chromium/iron-icon/iron-icon.js index 001d8d58..35835d2b 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-icon/iron-icon.js +++ b/third_party/polymer/v3_0/components-chromium/iron-icon/iron-icon.js
@@ -11,10 +11,10 @@ import '../iron-flex-layout/iron-flex-layout.js'; import {IronMeta} from '../iron-meta/iron-meta.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; -import {Base} from '../polymer/polymer-legacy.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; +import {Base} from '../polymer/polymer_bundled.min.js'; /**
diff --git a/third_party/polymer/v3_0/components-chromium/iron-iconset-svg/iron-iconset-svg.js b/third_party/polymer/v3_0/components-chromium/iron-iconset-svg/iron-iconset-svg.js index c76a530..9bd6935 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-iconset-svg/iron-iconset-svg.js +++ b/third_party/polymer/v3_0/components-chromium/iron-iconset-svg/iron-iconset-svg.js
@@ -8,11 +8,11 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronMeta} from '../iron-meta/iron-meta.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; /** * The `iron-iconset-svg` element allows users to define their own icon sets * that contain svg icons. The svg icon elements should be children of the
diff --git a/third_party/polymer/v3_0/components-chromium/iron-input/iron-input.js b/third_party/polymer/v3_0/components-chromium/iron-input/iron-input.js index 3c852791..2754f1ae 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-input/iron-input.js +++ b/third_party/polymer/v3_0/components-chromium/iron-input/iron-input.js
@@ -8,13 +8,13 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronA11yAnnouncer} from '../iron-a11y-announcer/iron-a11y-announcer.js'; import {IronValidatableBehavior} from '../iron-validatable-behavior/iron-validatable-behavior.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; /** `<iron-input>` is a wrapper to a native `<input>` element, that adds two-way
diff --git a/third_party/polymer/v3_0/components-chromium/iron-list/iron-list.js b/third_party/polymer/v3_0/components-chromium/iron-list/iron-list.js index 62f6768..28b9353 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-list/iron-list.js +++ b/third_party/polymer/v3_0/components-chromium/iron-list/iron-list.js
@@ -8,21 +8,21 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import '../iron-a11y-keys-behavior/iron-a11y-keys-behavior.js'; import {IronResizableBehavior} from '../iron-resizable-behavior/iron-resizable-behavior.js'; import {IronScrollTargetBehavior} from '../iron-scroll-target-behavior/iron-scroll-target-behavior.js'; -import {OptionalMutableDataBehavior} from '../polymer/lib/legacy/mutable-data-behavior.js'; -import {Polymer as Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {Templatizer} from '../polymer/lib/legacy/templatizer-behavior.js'; -import {animationFrame, idlePeriod, microTask} from '../polymer/lib/utils/async.js'; -import {Debouncer} from '../polymer/lib/utils/debounce.js'; -import {enqueueDebouncer, flush} from '../polymer/lib/utils/flush.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; -import {matches, translate} from '../polymer/lib/utils/path.js'; -import {TemplateInstanceBase} from '../polymer/lib/utils/templatize.js'; +import {OptionalMutableDataBehavior} from '../polymer/polymer_bundled.min.js'; +import {Polymer as Polymer} from '../polymer/polymer_bundled.min.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {Templatizer} from '../polymer/polymer_bundled.min.js'; +import {animationFrame, idlePeriod, microTask} from '../polymer/polymer_bundled.min.js'; +import {Debouncer} from '../polymer/polymer_bundled.min.js'; +import {enqueueDebouncer, flush} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; +import {matches, translate} from '../polymer/polymer_bundled.min.js'; +import {TemplateInstanceBase} from '../polymer/polymer_bundled.min.js'; var IOS = navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/); var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8;
diff --git a/third_party/polymer/v3_0/components-chromium/iron-location/iron-location.js b/third_party/polymer/v3_0/components-chromium/iron-location/iron-location.js index 5a402bf..89acb286 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-location/iron-location.js +++ b/third_party/polymer/v3_0/components-chromium/iron-location/iron-location.js
@@ -8,10 +8,10 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; /** The `iron-location` element manages binding to and from the current URL.
diff --git a/third_party/polymer/v3_0/components-chromium/iron-location/iron-query-params.js b/third_party/polymer/v3_0/components-chromium/iron-location/iron-query-params.js index 98e10b78d..c307d78 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-location/iron-query-params.js +++ b/third_party/polymer/v3_0/components-chromium/iron-location/iron-query-params.js
@@ -8,9 +8,9 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; /** * @demo demo/iron-query-params.html */
diff --git a/third_party/polymer/v3_0/components-chromium/iron-media-query/iron-media-query.js b/third_party/polymer/v3_0/components-chromium/iron-media-query/iron-media-query.js index 48cb839..2125b00 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-media-query/iron-media-query.js +++ b/third_party/polymer/v3_0/components-chromium/iron-media-query/iron-media-query.js
@@ -8,9 +8,9 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; /** `iron-media-query` can be used to data bind to a CSS media query.
diff --git a/third_party/polymer/v3_0/components-chromium/iron-meta/iron-meta.js b/third_party/polymer/v3_0/components-chromium/iron-meta/iron-meta.js index e32da21..acc3c2c 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-meta/iron-meta.js +++ b/third_party/polymer/v3_0/components-chromium/iron-meta/iron-meta.js
@@ -8,9 +8,9 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; export class IronMeta { /**
diff --git a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-focusables-helper.js b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-focusables-helper.js index 40c10a0..d2edaa0 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-focusables-helper.js +++ b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-focusables-helper.js
@@ -8,9 +8,9 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; var p = Element.prototype; var matches = p.matches || p.matchesSelector || p.mozMatchesSelector ||
diff --git a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-backdrop.js b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-backdrop.js index 8bd49271..fca79f0 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-backdrop.js +++ b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-backdrop.js
@@ -8,11 +8,11 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; /* `iron-overlay-backdrop` is a backdrop used by `Polymer.IronOverlayBehavior`. It
diff --git a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior.js index 0f215b1..6200ea12 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-behavior.js
@@ -8,12 +8,12 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronFitBehavior} from '../iron-fit-behavior/iron-fit-behavior.js'; import {IronResizableBehavior} from '../iron-resizable-behavior/iron-resizable-behavior.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {useShadow} from '../polymer/lib/utils/settings.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {useShadow} from '../polymer/polymer_bundled.min.js'; import {IronFocusablesHelper} from './iron-focusables-helper.js'; import {IronOverlayManager} from './iron-overlay-manager.js';
diff --git a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-manager.js b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-manager.js index 99768f4..ac72c52 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-manager.js +++ b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-overlay-manager.js
@@ -8,12 +8,12 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import './iron-overlay-backdrop.js'; import {IronA11yKeysBehavior} from '../iron-a11y-keys-behavior/iron-a11y-keys-behavior.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import * as gestures from '../polymer/lib/utils/gestures.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import * as gestures from '../polymer/polymer_bundled.min.js'; /** * @struct @@ -231,7 +231,7 @@ this.backdropElement.opened = !!overlay; // Property observers are not fired until element is attached // in Polymer 2.x, so we ensure element is attached if needed. - // https://github.com/Polymer/polymer/issues/4526 + // https://github.com/Polymer/polymer/polymer_bundled.min.js4526 this.backdropElement.prepare(); },
diff --git a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-scroll-manager.js b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-scroll-manager.js index 156d1435..247be1df 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-scroll-manager.js +++ b/third_party/polymer/v3_0/components-chromium/iron-overlay-behavior/iron-scroll-manager.js
@@ -8,9 +8,9 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; /** * Used to calculate the scroll direction during touch events. * @type {!Object}
diff --git a/third_party/polymer/v3_0/components-chromium/iron-pages/iron-pages.js b/third_party/polymer/v3_0/components-chromium/iron-pages/iron-pages.js index 2708d9a6..a99c00d 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-pages/iron-pages.js +++ b/third_party/polymer/v3_0/components-chromium/iron-pages/iron-pages.js
@@ -8,12 +8,12 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronResizableBehavior} from '../iron-resizable-behavior/iron-resizable-behavior.js'; import {IronSelectableBehavior} from '../iron-selector/iron-selectable.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; /** `iron-pages` is used to select one of its children to show. One use is to cycle
diff --git a/third_party/polymer/v3_0/components-chromium/iron-range-behavior/iron-range-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-range-behavior/iron-range-behavior.js index 1802854..ec84de0 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-range-behavior/iron-range-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/iron-range-behavior/iron-range-behavior.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; /** * `iron-range-behavior` provides the behavior for something with a minimum to
diff --git a/third_party/polymer/v3_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior.js index 8a5317e..0dc27e4 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/iron-resizable-behavior/iron-resizable-behavior.js
@@ -8,10 +8,10 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {useShadow} from '../polymer/lib/utils/settings.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {useShadow} from '../polymer/polymer_bundled.min.js'; // Contains all connected resizables that do not have a parent. var ORPHANS = new Set();
diff --git a/third_party/polymer/v3_0/components-chromium/iron-scroll-target-behavior/iron-scroll-target-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-scroll-target-behavior/iron-scroll-target-behavior.js index a9d889bdf..14118df 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-scroll-target-behavior/iron-scroll-target-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/iron-scroll-target-behavior/iron-scroll-target-behavior.js
@@ -8,9 +8,9 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; /** * `Polymer.IronScrollTargetBehavior` allows an element to respond to scroll
diff --git a/third_party/polymer/v3_0/components-chromium/iron-scroll-threshold/iron-scroll-threshold.js b/third_party/polymer/v3_0/components-chromium/iron-scroll-threshold/iron-scroll-threshold.js index c592f93..a5b87ed 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-scroll-threshold/iron-scroll-threshold.js +++ b/third_party/polymer/v3_0/components-chromium/iron-scroll-threshold/iron-scroll-threshold.js
@@ -8,11 +8,11 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronScrollTargetBehavior} from '../iron-scroll-target-behavior/iron-scroll-target-behavior.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; /** `iron-scroll-threshold` is a utility element that listens for `scroll` events
diff --git a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-multi-selectable.js b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-multi-selectable.js index 9a797323..9acb77a 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-multi-selectable.js +++ b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-multi-selectable.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronSelectableBehavior} from './iron-selectable.js';
diff --git a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selectable.js b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selectable.js index 9a4cd9e6..e18d709fa 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selectable.js +++ b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selectable.js
@@ -8,10 +8,10 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {dashToCamelCase} from '../polymer/lib/utils/case-map.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {dashToCamelCase} from '../polymer/polymer_bundled.min.js'; import {IronSelection} from './iron-selection.js';
diff --git a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selection.js b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selection.js index ebdc9e2b..3788264 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selection.js +++ b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selection.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; export class IronSelection { /**
diff --git a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selector.js b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selector.js index 1991f583..b12ab298 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selector.js +++ b/third_party/polymer/v3_0/components-chromium/iron-selector/iron-selector.js
@@ -8,9 +8,9 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; import {IronMultiSelectableBehavior} from './iron-multi-selectable.js';
diff --git a/third_party/polymer/v3_0/components-chromium/iron-test-helpers/mock-interactions.js b/third_party/polymer/v3_0/components-chromium/iron-test-helpers/mock-interactions.js index 13b9380..86b19e61b 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-test-helpers/mock-interactions.js +++ b/third_party/polymer/v3_0/components-chromium/iron-test-helpers/mock-interactions.js
@@ -9,7 +9,7 @@ * rights grant found at http://polymer.github.io/PATENTS.txt */ -import {Base} from '../polymer/polymer-legacy.js'; +import {Base} from '../polymer/polymer_bundled.min.js'; const HAS_NEW_MOUSE = (() => { let has = false;
diff --git a/third_party/polymer/v3_0/components-chromium/iron-test-helpers/test-helpers.js b/third_party/polymer/v3_0/components-chromium/iron-test-helpers/test-helpers.js index af80627..d490b7f 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-test-helpers/test-helpers.js +++ b/third_party/polymer/v3_0/components-chromium/iron-test-helpers/test-helpers.js
@@ -9,7 +9,7 @@ * rights grant found at http://polymer.github.io/PATENTS.txt */ -import {dom, flush} from '../polymer/lib/legacy/polymer.dom.js'; +import {dom, flush} from '../polymer/polymer_bundled.min.js'; /** * Forces distribution of light children, and lifecycle callbacks on the
diff --git a/third_party/polymer/v3_0/components-chromium/iron-validatable-behavior/iron-validatable-behavior.js b/third_party/polymer/v3_0/components-chromium/iron-validatable-behavior/iron-validatable-behavior.js index c0cf7479..7e3dc51 100644 --- a/third_party/polymer/v3_0/components-chromium/iron-validatable-behavior/iron-validatable-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/iron-validatable-behavior/iron-validatable-behavior.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronMeta} from '../iron-meta/iron-meta.js';
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-in-animation.js b/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-in-animation.js index 257320c..51de8dd6 100644 --- a/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-in-animation.js +++ b/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-in-animation.js
@@ -8,9 +8,9 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../../polymer/polymer-legacy.js'; +import '../../polymer/polymer_bundled.min.js'; -import {Polymer} from '../../polymer/lib/legacy/polymer-fn.js'; +import {Polymer} from '../../polymer/polymer_bundled.min.js'; import {NeonAnimationBehavior} from '../neon-animation-behavior.js'; /* `<fade-in-animation>` animates the opacity of an element from 0 to 1.
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-out-animation.js b/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-out-animation.js index 83c586e..de7e04a 100644 --- a/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-out-animation.js +++ b/third_party/polymer/v3_0/components-chromium/neon-animation/animations/fade-out-animation.js
@@ -8,9 +8,9 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../../polymer/polymer-legacy.js'; +import '../../polymer/polymer_bundled.min.js'; -import {Polymer} from '../../polymer/lib/legacy/polymer-fn.js'; +import {Polymer} from '../../polymer/polymer_bundled.min.js'; import {NeonAnimationBehavior} from '../neon-animation-behavior.js'; /* `<fade-out-animation>` animates the opacity of an element from 1 to 0.
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable-behavior.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable-behavior.js index 14c4709..e8ab42f 100644 --- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable-behavior.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; /** * `NeonAnimatableBehavior` is implemented by elements containing
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable.js index 215593d..cba6aeb 100644 --- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable.js +++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animatable.js
@@ -8,11 +8,11 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronResizableBehavior} from '../iron-resizable-behavior/iron-resizable-behavior.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; import {NeonAnimatableBehavior} from './neon-animatable-behavior.js';
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animated-pages.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animated-pages.js index 0ea3e08..d4ee33e 100644 --- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animated-pages.js +++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animated-pages.js
@@ -8,13 +8,13 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronResizableBehavior} from '../iron-resizable-behavior/iron-resizable-behavior.js'; import {IronSelectableBehavior} from '../iron-selector/iron-selectable.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; import {NeonAnimationRunnerBehavior} from './neon-animation-runner-behavior.js';
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-behavior.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-behavior.js index 33be9de..5897c90 100644 --- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-behavior.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; /** * Use `NeonAnimationBehavior` to implement an animation.
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-runner-behavior.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-runner-behavior.js index c718e87..6749b5b3 100644 --- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-runner-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-animation-runner-behavior.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {NeonAnimatableBehavior} from './neon-animatable-behavior.js';
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animatable-behavior.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animatable-behavior.js index 9f8b10f..e1036c7 100644 --- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animatable-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animatable-behavior.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {NeonAnimatableBehavior} from './neon-animatable-behavior.js';
diff --git a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animation-behavior.js b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animation-behavior.js index 9343824..a92bc67 100644 --- a/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animation-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/neon-animation/neon-shared-element-animation-behavior.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {NeonAnimationBehavior} from './neon-animation-behavior.js';
diff --git a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-button-behavior.js b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-button-behavior.js index 23c9908a..158a366 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-button-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-button-behavior.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronButtonState, IronButtonStateImpl} from '../iron-behaviors/iron-button-state.js'; import {IronControlState} from '../iron-behaviors/iron-control-state.js';
diff --git a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-inky-focus-behavior.js b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-inky-focus-behavior.js index 73a141bf..8e5a3cc 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-inky-focus-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-inky-focus-behavior.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronButtonState} from '../iron-behaviors/iron-button-state.js'; import {IronControlState} from '../iron-behaviors/iron-control-state.js';
diff --git a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-ripple-behavior.js b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-ripple-behavior.js index 7976f0b2..6aba337 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-ripple-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-ripple-behavior.js
@@ -8,11 +8,11 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import '../paper-ripple/paper-ripple.js'; import {IronButtonStateImpl} from '../iron-behaviors/iron-button-state.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; /** * `PaperRippleBehavior` dynamically implements a ripple when the element has
diff --git a/third_party/polymer/v3_0/components-chromium/paper-button/paper-button.js b/third_party/polymer/v3_0/components-chromium/paper-button/paper-button.js index eb9535c..42dc7218 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-button/paper-button.js +++ b/third_party/polymer/v3_0/components-chromium/paper-button/paper-button.js
@@ -12,8 +12,8 @@ import '../paper-styles/element-styles/paper-material-styles.js'; import {PaperButtonBehavior, PaperButtonBehaviorImpl} from '../paper-behaviors/paper-button-behavior.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {html} from '../polymer/polymer-legacy.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; const template = html` <style include="paper-material-styles">
diff --git a/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-addon-behavior.js b/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-addon-behavior.js index 1f7e4b5a..b2dae3c 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-addon-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-addon-behavior.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; /** * Use `Polymer.PaperInputAddonBehavior` to implement an add-on for
diff --git a/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-container.js b/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-container.js index 3e724e7e..2777784f 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-container.js +++ b/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-container.js
@@ -8,15 +8,15 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import '../iron-flex-layout/iron-flex-layout.js'; import '../paper-styles/default-theme.js'; import '../paper-styles/typography.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {dashToCamelCase} from '../polymer/lib/utils/case-map.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {dashToCamelCase} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; const template = html` <custom-style> <style is="custom-style">
diff --git a/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-error.js b/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-error.js index afd9adc..97589a32 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-error.js +++ b/third_party/polymer/v3_0/components-chromium/paper-input/paper-input-error.js
@@ -8,12 +8,12 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import '../paper-styles/default-theme.js'; import '../paper-styles/typography.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; import {PaperInputAddonBehavior} from './paper-input-addon-behavior.js';
diff --git a/third_party/polymer/v3_0/components-chromium/paper-progress/paper-progress.js b/third_party/polymer/v3_0/components-chromium/paper-progress/paper-progress.js index fdb26f7..53a221c 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-progress/paper-progress.js +++ b/third_party/polymer/v3_0/components-chromium/paper-progress/paper-progress.js
@@ -8,13 +8,13 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import '../iron-flex-layout/iron-flex-layout.js'; import '../paper-styles/color.js'; import {IronRangeBehavior} from '../iron-range-behavior/iron-range-behavior.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; /** Material design: [Progress &
diff --git a/third_party/polymer/v3_0/components-chromium/paper-ripple/paper-ripple.js b/third_party/polymer/v3_0/components-chromium/paper-ripple/paper-ripple.js index 6969cd7..0288d836 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-ripple/paper-ripple.js +++ b/third_party/polymer/v3_0/components-chromium/paper-ripple/paper-ripple.js
@@ -8,12 +8,12 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import {IronA11yKeysBehavior} from '../iron-a11y-keys-behavior/iron-a11y-keys-behavior.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; var Utility = { distance: function(x1, y1, x2, y2) {
diff --git a/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-behavior.js b/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-behavior.js index ebc060e..8ab7c52 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-behavior.js +++ b/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-behavior.js
@@ -8,7 +8,7 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; /** @polymerBehavior */ export const PaperSpinnerBehavior = {
diff --git a/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-lite.js b/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-lite.js index 740be377..2577e3a 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-lite.js +++ b/third_party/polymer/v3_0/components-chromium/paper-spinner/paper-spinner-lite.js
@@ -8,12 +8,12 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import '../paper-styles/color.js'; import './paper-spinner-styles.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; import {PaperSpinnerBehavior} from './paper-spinner-behavior.js';
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/classes/global.js b/third_party/polymer/v3_0/components-chromium/paper-styles/classes/global.js index 2082c8f..9a4d2e2 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-styles/classes/global.js +++ b/third_party/polymer/v3_0/components-chromium/paper-styles/classes/global.js
@@ -17,7 +17,7 @@ import '../paper-styles-classes.js'; -import {html} from '../../polymer/lib/utils/html-tag.js'; +import {html} from '../../polymer/polymer_bundled.min.js'; const template = html`<style> /* Mixins */
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/classes/shadow.js b/third_party/polymer/v3_0/components-chromium/paper-styles/classes/shadow.js index 44dcd290..a88a6e17 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-styles/classes/shadow.js +++ b/third_party/polymer/v3_0/components-chromium/paper-styles/classes/shadow.js
@@ -17,7 +17,7 @@ For a set of styles that can be applied to an element, check paper-styles/shadow.js. */ -import {html} from '../../polymer/lib/utils/html-tag.js'; +import {html} from '../../polymer/polymer_bundled.min.js'; const template = html` <style> .shadow-transition {
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/classes/typography.js b/third_party/polymer/v3_0/components-chromium/paper-styles/classes/typography.js index b35f889..0c397e0 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-styles/classes/typography.js +++ b/third_party/polymer/v3_0/components-chromium/paper-styles/classes/typography.js
@@ -18,7 +18,7 @@ paper-styles/typography.html. */ import '../../font-roboto/roboto.js'; -import {html} from '../../polymer/lib/utils/html-tag.js'; +import {html} from '../../polymer/polymer_bundled.min.js'; const template = html` <style>
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/color.js b/third_party/polymer/v3_0/components-chromium/paper-styles/color.js index dac6530..0105d3a5 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-styles/color.js +++ b/third_party/polymer/v3_0/components-chromium/paper-styles/color.js
@@ -9,9 +9,9 @@ found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {html} from '../polymer/polymer_bundled.min.js'; const template = html` <custom-style> <style is="custom-style">
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/default-theme.js b/third_party/polymer/v3_0/components-chromium/paper-styles/default-theme.js index 59533eb..9b33502 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-styles/default-theme.js +++ b/third_party/polymer/v3_0/components-chromium/paper-styles/default-theme.js
@@ -11,10 +11,10 @@ /* Taken from * https://www.google.com/design/spec/style/color.html#color-ui-color-application */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import './color.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {html} from '../polymer/polymer_bundled.min.js'; const template = html` <custom-style> <style is="custom-style">
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/demo-pages.js b/third_party/polymer/v3_0/components-chromium/paper-styles/demo-pages.js index 2cfb51b..d90c8a9 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-styles/demo-pages.js +++ b/third_party/polymer/v3_0/components-chromium/paper-styles/demo-pages.js
@@ -18,13 +18,13 @@ check iron-demo-helpers/demo-pages-shared-styles.html. */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import '../iron-flex-layout/iron-flex-layout.js'; import './color.js'; import './typography.js'; import './shadow.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {html} from '../polymer/polymer_bundled.min.js'; const template = html`<custom-style> <style is="custom-style"> body {
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-item-styles.js b/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-item-styles.js index 3d9d2ab..042f89bc 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-item-styles.js +++ b/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-item-styles.js
@@ -29,12 +29,12 @@ @demo demo/index.html */ -import '../../polymer/polymer-legacy.js'; +import '../../polymer/polymer_bundled.min.js'; import '../color.js'; import '../default-theme.js'; import '../typography.js'; -import {html} from '../../polymer/lib/utils/html-tag.js'; +import {html} from '../../polymer/polymer_bundled.min.js'; const template = html` <dom-module id="paper-item-styles"> <template>
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-material-styles.js b/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-material-styles.js index d2e3e08..298b1ab 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-material-styles.js +++ b/third_party/polymer/v3_0/components-chromium/paper-styles/element-styles/paper-material-styles.js
@@ -29,10 +29,10 @@ @demo demo/index.html */ -import '../../polymer/polymer-legacy.js'; +import '../../polymer/polymer_bundled.min.js'; import '../shadow.js'; -import {html} from '../../polymer/lib/utils/html-tag.js'; +import {html} from '../../polymer/polymer_bundled.min.js'; const template = html` <dom-module id="paper-material-styles"> <template>
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/shadow.js b/third_party/polymer/v3_0/components-chromium/paper-styles/shadow.js index 1e7eb944..36f66c54 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-styles/shadow.js +++ b/third_party/polymer/v3_0/components-chromium/paper-styles/shadow.js
@@ -9,9 +9,9 @@ found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {html} from '../polymer/polymer_bundled.min.js'; const template = html` <custom-style> <style is="custom-style">
diff --git a/third_party/polymer/v3_0/components-chromium/paper-styles/typography.js b/third_party/polymer/v3_0/components-chromium/paper-styles/typography.js index ec805f0..ed930d05 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-styles/typography.js +++ b/third_party/polymer/v3_0/components-chromium/paper-styles/typography.js
@@ -17,10 +17,10 @@ Design typography section. */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; import '../font-roboto/roboto.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {html} from '../polymer/polymer_bundled.min.js'; const template = html`<custom-style> <style is="custom-style"> html {
diff --git a/third_party/polymer/v3_0/components-chromium/paper-tooltip/paper-tooltip.js b/third_party/polymer/v3_0/components-chromium/paper-tooltip/paper-tooltip.js index 1599a28..aaa6e15 100644 --- a/third_party/polymer/v3_0/components-chromium/paper-tooltip/paper-tooltip.js +++ b/third_party/polymer/v3_0/components-chromium/paper-tooltip/paper-tooltip.js
@@ -8,11 +8,11 @@ part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -import '../polymer/polymer-legacy.js'; +import '../polymer/polymer_bundled.min.js'; -import {Polymer} from '../polymer/lib/legacy/polymer-fn.js'; -import {dom} from '../polymer/lib/legacy/polymer.dom.js'; -import {html} from '../polymer/lib/utils/html-tag.js'; +import {Polymer} from '../polymer/polymer_bundled.min.js'; +import {dom} from '../polymer/polymer_bundled.min.js'; +import {html} from '../polymer/polymer_bundled.min.js'; /** Material design:
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/closure-types.js b/third_party/polymer/v3_0/components-chromium/polymer/externs/closure-types.js deleted file mode 100644 index 86ddb08..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/externs/closure-types.js +++ /dev/null
@@ -1,1588 +0,0 @@ -/** - * @fileoverview Generated typings for Polymer mixins - * @externs - * @suppress {checkPrototypalTypes} - * - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt - */ -/* eslint-disable */ -/** -* @interface -*/ -function Polymer_PropertiesChanged(){} -/** @type {undefined} */ -Polymer_PropertiesChanged.prototype.__dataEnabled; - -/** -* @override -* @param {string} property Name of the property -* @param {boolean=} readOnly When true, no setter is created; the - protected `_setProperty` function must be used to set the property -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._createPropertyAccessor = function(property, readOnly){}; -/** -* @override -* @param {string} property Name of the property -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._addPropertyToAttributeMap = function(property){}; -/** -* @override -* @param {string} property Name of the property -* @param {boolean=} readOnly When true, no setter is created -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._definePropertyAccessor = function(property, readOnly){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertiesChanged.prototype.ready = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._initializeProperties = function(){}; -/** -* @override -* @param {Object} props Bag of property values that were overwritten - when creating property accessors. -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._initializeInstanceProperties = function(props){}; -/** -* @override -* @param {string} property Name of the property -* @param {*} value Value to set -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._setProperty = function(property, value){}; -/** -* @override -* @param {string} property Name of property -* @return {*} -*/ -Polymer_PropertiesChanged.prototype._getProperty = function(property){}; -/** -* @override -* @param {string} property Name of the property -* @param {*} value Value to set -* @param {boolean=} ext Not used here; affordance for closure -* @return {boolean} -*/ -Polymer_PropertiesChanged.prototype._setPendingProperty = function(property, value, ext){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._invalidateProperties = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._enableProperties = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._flushProperties = function(){}; -/** -* @override -* @param {!Object} currentProps Bag of all current accessor values -* @param {?Object} changedProps Bag of properties changed since the last - call to `_propertiesChanged` -* @param {?Object} oldProps Bag of previous values for each property - in `changedProps` -* @return {boolean} -*/ -Polymer_PropertiesChanged.prototype._shouldPropertiesChange = function(currentProps, changedProps, oldProps){}; -/** -* @override -* @param {!Object} currentProps Bag of all current accessor values -* @param {?Object} changedProps Bag of properties changed since the last - call to `_propertiesChanged` -* @param {?Object} oldProps Bag of previous values for each property - in `changedProps` -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._propertiesChanged = function(currentProps, changedProps, oldProps){}; -/** -* @override -* @param {string} property Property name -* @param {*} value New property value -* @param {*} old Previous property value -* @return {boolean} -*/ -Polymer_PropertiesChanged.prototype._shouldPropertyChange = function(property, value, old){}; -/** -* @override -* @param {string} name Name of attribute that changed -* @param {?string} old Old attribute value -* @param {?string} value New attribute value -* @param {?string=} namespace Attribute namespace. -* @return {void} -*/ -Polymer_PropertiesChanged.prototype.attributeChangedCallback = function(name, old, value, namespace){}; -/** -* @override -* @param {string} attribute Name of attribute to deserialize. -* @param {?string} value of the attribute. -* @param {*=} type type to deserialize to, defaults to the value -returned from `typeForProperty` -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._attributeToProperty = function(attribute, value, type){}; -/** -* @override -* @param {string} property Property name to reflect. -* @param {string=} attribute Attribute name to reflect to. -* @param {*=} value Property value to refect. -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._propertyToAttribute = function(property, attribute, value){}; -/** -* @override -* @param {Element} node Element to set attribute to. -* @param {*} value Value to serialize. -* @param {string} attribute Attribute name to serialize to. -* @return {void} -*/ -Polymer_PropertiesChanged.prototype._valueToNodeAttribute = function(node, value, attribute){}; -/** -* @override -* @param {*} value Property value to serialize. -* @return {(string | undefined)} -*/ -Polymer_PropertiesChanged.prototype._serializeValue = function(value){}; -/** -* @override -* @param {?string} value Value to deserialize. -* @param {*=} type Type to deserialize the string to. -* @return {*} -*/ -Polymer_PropertiesChanged.prototype._deserializeValue = function(value, type){}; -/** -* @param {!Object} props Object whose keys are names of accessors. -* @return {void} -*/ -Polymer_PropertiesChanged.createProperties = function(props){}; -/** -* @param {string} property Property to convert -* @return {string} -*/ -Polymer_PropertiesChanged.attributeNameForProperty = function(property){}; -/** -* @param {string} name Name of property -* @return {void} -*/ -Polymer_PropertiesChanged.typeForProperty = function(name){}; -/** -* @interface -* @extends {Polymer_PropertiesChanged} -*/ -function Polymer_PropertyAccessors(){} -/** -* @override -* @param {string} property Name of the property -* @param {boolean=} readOnly When true, no setter is created - -When calling on a prototype, any overwritten values are saved in -`__dataProto`, and it is up to the subclasser to decide how/when -to set those properties back into the accessor. When calling on an -instance, the overwritten value is set via `_setPendingProperty`, -and the user should call `_invalidateProperties` or `_flushProperties` -for the values to take effect. -* @return {void} -*/ -Polymer_PropertyAccessors.prototype._definePropertyAccessor = function(property, readOnly){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertyAccessors.prototype._initializeProperties = function(){}; -/** -* @override -* @param {*} value Property value to serialize. -* @return {(string | undefined)} -*/ -Polymer_PropertyAccessors.prototype._serializeValue = function(value){}; -/** -* @override -* @param {?string} value Attribute value to deserialize. -* @param {*=} type Type to deserialize the string to. -* @return {*} -*/ -Polymer_PropertyAccessors.prototype._deserializeValue = function(value, type){}; -/** -* @override -* @param {Object} props Bag of property values that were overwritten - when creating property accessors. -* @return {void} -*/ -Polymer_PropertyAccessors.prototype._initializeProtoProperties = function(props){}; -/** -* @override -* @param {string} attribute Name of attribute to ensure is set. -* @param {string} value of the attribute. -* @return {void} -*/ -Polymer_PropertyAccessors.prototype._ensureAttribute = function(attribute, value){}; -/** -* @override -* @param {string} property Property name -* @return {boolean} -*/ -Polymer_PropertyAccessors.prototype._hasAccessor = function(property){}; -/** -* @override -* @param {string} prop Property name -* @return {boolean} -*/ -Polymer_PropertyAccessors.prototype._isPropertyPending = function(prop){}; -/** -* @param {string} property Property to convert -* @return {string} -*/ -Polymer_PropertyAccessors.attributeNameForProperty = function(property){}; -/** -* @return {void} -*/ -Polymer_PropertyAccessors.createPropertiesForAttributes = function(){}; -/** -* @interface -*/ -function Polymer_TemplateStamp(){} -/** -* @override -* @param {!HTMLTemplateElement} template Template to stamp -* @return {!StampedTemplate} -*/ -Polymer_TemplateStamp.prototype._stampTemplate = function(template){}; -/** -* @override -* @param {!EventTarget} node Node to add listener on -* @param {string} eventName Name of event -* @param {string} methodName Name of method -* @param {*=} context Context the method will be called on (defaults - to `node`) -* @return {Function} -*/ -Polymer_TemplateStamp.prototype._addMethodEventListenerToNode = function(node, eventName, methodName, context){}; -/** -* @override -* @param {!EventTarget} node Node to add event listener to -* @param {string} eventName Name of event -* @param {function (!Event): void} handler Listener function to add -* @return {void} -*/ -Polymer_TemplateStamp.prototype._addEventListenerToNode = function(node, eventName, handler){}; -/** -* @override -* @param {!EventTarget} node Node to remove event listener from -* @param {string} eventName Name of event -* @param {function (!Event): void} handler Listener function to remove -* @return {void} -*/ -Polymer_TemplateStamp.prototype._removeEventListenerFromNode = function(node, eventName, handler){}; -/** -* @param {!HTMLTemplateElement} template Template to parse -* @param {TemplateInfo=} outerTemplateInfo Template metadata from the outer - template, for parsing nested templates -* @return {!TemplateInfo} -*/ -Polymer_TemplateStamp._parseTemplate = function(template, outerTemplateInfo){}; -/** -* @param {*} template -* @param {*} templateInfo -* @param {*} nodeInfo -*/ -Polymer_TemplateStamp._parseTemplateContent = function(template, templateInfo, nodeInfo){}; -/** -* @param {Node} node Node to parse -* @param {!TemplateInfo} templateInfo Template metadata for current template -* @param {!NodeInfo} nodeInfo Node metadata for current template. -* @return {boolean} -*/ -Polymer_TemplateStamp._parseTemplateNode = function(node, templateInfo, nodeInfo){}; -/** -* @param {Node} root Root node whose `childNodes` will be parsed -* @param {!TemplateInfo} templateInfo Template metadata for current template -* @param {!NodeInfo} nodeInfo Node metadata for current template. -* @return {void} -*/ -Polymer_TemplateStamp._parseTemplateChildNodes = function(root, templateInfo, nodeInfo){}; -/** -* @param {HTMLTemplateElement} node Node to parse (a <template>) -* @param {TemplateInfo} outerTemplateInfo Template metadata for current template - that includes the template `node` -* @param {!NodeInfo} nodeInfo Node metadata for current template. -* @return {boolean} -*/ -Polymer_TemplateStamp._parseTemplateNestedTemplate = function(node, outerTemplateInfo, nodeInfo){}; -/** -* @param {Element} node Node to parse -* @param {TemplateInfo} templateInfo Template metadata for current template -* @param {NodeInfo} nodeInfo Node metadata for current template. -* @return {boolean} -*/ -Polymer_TemplateStamp._parseTemplateNodeAttributes = function(node, templateInfo, nodeInfo){}; -/** -* @param {Element} node Node to parse -* @param {!TemplateInfo} templateInfo Template metadata for current template -* @param {!NodeInfo} nodeInfo Node metadata for current template. -* @param {string} name Attribute name -* @param {string} value Attribute value -* @return {boolean} -*/ -Polymer_TemplateStamp._parseTemplateNodeAttribute = function(node, templateInfo, nodeInfo, name, value){}; -/** -* @param {HTMLTemplateElement} template Template to retrieve `content` for -* @return {DocumentFragment} -*/ -Polymer_TemplateStamp._contentForTemplate = function(template){}; -/** -* @interface -* @extends {Polymer_TemplateStamp} -* @extends {Polymer_PropertyAccessors} -*/ -function Polymer_PropertyEffects(){} -/** @type {boolean} */ -Polymer_PropertyEffects.prototype.__dataClientsReady; - -/** @type {Array} */ -Polymer_PropertyEffects.prototype.__dataPendingClients; - -/** @type {Object} */ -Polymer_PropertyEffects.prototype.__dataToNotify; - -/** @type {Object} */ -Polymer_PropertyEffects.prototype.__dataLinkedPaths; - -/** @type {boolean} */ -Polymer_PropertyEffects.prototype.__dataHasPaths; - -/** @type {Object} */ -Polymer_PropertyEffects.prototype.__dataCompoundStorage; - -/** @type {Polymer_PropertyEffects} */ -Polymer_PropertyEffects.prototype.__dataHost; - -/** @type {!Object} */ -Polymer_PropertyEffects.prototype.__dataTemp; - -/** @type {boolean} */ -Polymer_PropertyEffects.prototype.__dataClientsInitialized; - -/** @type {!Object} */ -Polymer_PropertyEffects.prototype.__data; - -/** @type {(!Object | null)} */ -Polymer_PropertyEffects.prototype.__dataPending; - -/** @type {!Object} */ -Polymer_PropertyEffects.prototype.__dataOld; - -/** @type {Object} */ -Polymer_PropertyEffects.prototype.__computeEffects; - -/** @type {Object} */ -Polymer_PropertyEffects.prototype.__reflectEffects; - -/** @type {Object} */ -Polymer_PropertyEffects.prototype.__notifyEffects; - -/** @type {Object} */ -Polymer_PropertyEffects.prototype.__propagateEffects; - -/** @type {Object} */ -Polymer_PropertyEffects.prototype.__observeEffects; - -/** @type {Object} */ -Polymer_PropertyEffects.prototype.__readOnly; - -/** @type {!TemplateInfo} */ -Polymer_PropertyEffects.prototype.__templateInfo; - -/** @type {undefined} */ -Polymer_PropertyEffects.prototype.PROPERTY_EFFECT_TYPES; - -/** -* @override -* @param {!HTMLTemplateElement} template Template to stamp -* @return {!StampedTemplate} -*/ -Polymer_PropertyEffects.prototype._stampTemplate = function(template){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertyEffects.prototype.ready = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertyEffects.prototype._initializeProperties = function(){}; -/** -* @override -* @param {Object} props Properties to initialize on the instance -* @return {void} -*/ -Polymer_PropertyEffects.prototype._initializeInstanceProperties = function(props){}; -/** -* @override -* @param {string} property Name of the property -* @param {*} value Value to set -* @return {void} -*/ -Polymer_PropertyEffects.prototype._setProperty = function(property, value){}; -/** -* @override -* @param {string} property Name of the property -* @param {*} value Value to set -* @param {boolean=} shouldNotify True if property should fire notification - event (applies only for `notify: true` properties) -* @return {boolean} -*/ -Polymer_PropertyEffects.prototype._setPendingProperty = function(property, value, shouldNotify){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertyEffects.prototype._invalidateProperties = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertyEffects.prototype._flushProperties = function(){}; -/** -* @override -* @param {!Object} currentProps Bag of all current accessor values -* @param {?Object} changedProps Bag of properties changed since the last - call to `_propertiesChanged` -* @param {?Object} oldProps Bag of previous values for each property - in `changedProps` -* @return {void} -*/ -Polymer_PropertyEffects.prototype._propertiesChanged = function(currentProps, changedProps, oldProps){}; -/** -* @override -* @param {Object} props Properties to initialize on the prototype -* @return {void} -*/ -Polymer_PropertyEffects.prototype._initializeProtoProperties = function(props){}; -/** -* @override -* @param {string} property Property that should trigger the effect -* @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES -* @param {Object=} effect Effect metadata object -* @return {void} -*/ -Polymer_PropertyEffects.prototype._addPropertyEffect = function(property, type, effect){}; -/** -* @override -* @param {string} property Property the effect was associated with -* @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES -* @param {Object=} effect Effect metadata object to remove -* @return {void} -*/ -Polymer_PropertyEffects.prototype._removePropertyEffect = function(property, type, effect){}; -/** -* @override -* @param {string} property Property name -* @param {string=} type Effect type, from this.PROPERTY_EFFECT_TYPES -* @return {boolean} -*/ -Polymer_PropertyEffects.prototype._hasPropertyEffect = function(property, type){}; -/** -* @override -* @param {string} property Property name -* @return {boolean} -*/ -Polymer_PropertyEffects.prototype._hasReadOnlyEffect = function(property){}; -/** -* @override -* @param {string} property Property name -* @return {boolean} -*/ -Polymer_PropertyEffects.prototype._hasNotifyEffect = function(property){}; -/** -* @override -* @param {string} property Property name -* @return {boolean} -*/ -Polymer_PropertyEffects.prototype._hasReflectEffect = function(property){}; -/** -* @override -* @param {string} property Property name -* @return {boolean} -*/ -Polymer_PropertyEffects.prototype._hasComputedEffect = function(property){}; -/** -* @override -* @param {(string | !Array.<(number | string)>)} path Path to set -* @param {*} value Value to set -* @param {boolean=} shouldNotify Set to true if this change should - cause a property notification event dispatch -* @param {boolean=} isPathNotification If the path being set is a path - notification of an already changed value, as opposed to a request - to set and notify the change. In the latter `false` case, a dirty - check is performed and then the value is set to the path before - enqueuing the pending property change. -* @return {boolean} -*/ -Polymer_PropertyEffects.prototype._setPendingPropertyOrPath = function(path, value, shouldNotify, isPathNotification){}; -/** -* @override -* @param {!Node} node The node to set a property on -* @param {string} prop The property to set -* @param {*} value The value to set -* @return {void} -*/ -Polymer_PropertyEffects.prototype._setUnmanagedPropertyToNode = function(node, prop, value){}; -/** -* @override -* @param {Object} client PropertyEffects client to enqueue -* @return {void} -*/ -Polymer_PropertyEffects.prototype._enqueueClient = function(client){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertyEffects.prototype._flushClients = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertyEffects.prototype._readyClients = function(){}; -/** -* @override -* @param {Object} props Bag of one or more key-value pairs whose key is - a property and value is the new value to set for that property. -* @param {boolean=} setReadOnly When true, any private values set in - `props` will be set. By default, `setProperties` will not set - `readOnly: true` root properties. -* @return {void} -*/ -Polymer_PropertyEffects.prototype.setProperties = function(props, setReadOnly){}; -/** -* @override -* @param {Object} changedProps Bag of changed properties -* @param {Object} oldProps Bag of previous values for changed properties -* @param {boolean} hasPaths True with `props` contains one or more paths -* @return {void} -*/ -Polymer_PropertyEffects.prototype._propagatePropertyChanges = function(changedProps, oldProps, hasPaths){}; -/** -* @override -* @param {(string | !Array.<(string | number)>)} to Target path to link. -* @param {(string | !Array.<(string | number)>)} from Source path to link. -* @return {void} -*/ -Polymer_PropertyEffects.prototype.linkPaths = function(to, from){}; -/** -* @override -* @param {(string | !Array.<(string | number)>)} path Target path to unlink. -* @return {void} -*/ -Polymer_PropertyEffects.prototype.unlinkPaths = function(path){}; -/** -* @override -* @param {string} path Path that should be notified. -* @param {Array} splices Array of splice records indicating ordered - changes that occurred to the array. Each record should have the - following fields: - * index: index at which the change occurred - * removed: array of items that were removed from this index - * addedCount: number of new items added at this index - * object: a reference to the array in question - * type: the string literal 'splice' - - Note that splice records _must_ be normalized such that they are - reported in index order (raw results from `Object.observe` are not - ordered and must be normalized/merged before notifying). -* @return {void} -*/ -Polymer_PropertyEffects.prototype.notifySplices = function(path, splices){}; -/** -* @override -* @param {(string | !Array.<(string | number)>)} path Path to the value - to read. The path may be specified as a string (e.g. `foo.bar.baz`) - or an array of path parts (e.g. `['foo.bar', 'baz']`). Note that - bracketed expressions are not supported; string-based path parts - *must* be separated by dots. Note that when dereferencing array - indices, the index may be used as a dotted part directly - (e.g. `users.12.name` or `['users', 12, 'name']`). -* @param {Object=} root Root object from which the path is evaluated. -* @return {*} -*/ -Polymer_PropertyEffects.prototype.get = function(path, root){}; -/** -* @override -* @param {(string | !Array.<(string | number)>)} path Path to the value - to write. The path may be specified as a string (e.g. `'foo.bar.baz'`) - or an array of path parts (e.g. `['foo.bar', 'baz']`). Note that - bracketed expressions are not supported; string-based path parts - *must* be separated by dots. Note that when dereferencing array - indices, the index may be used as a dotted part directly - (e.g. `'users.12.name'` or `['users', 12, 'name']`). -* @param {*} value Value to set at the specified path. -* @param {Object=} root Root object from which the path is evaluated. - When specified, no notification will occur. -* @return {void} -*/ -Polymer_PropertyEffects.prototype.set = function(path, value, root){}; -/** -* @override -* @param {(string | !Array.<(string | number)>)} path Path to array. -* @param {...*} items Items to push onto array -* @return {number} -*/ -Polymer_PropertyEffects.prototype.push = function(path, items){}; -/** -* @override -* @param {(string | !Array.<(string | number)>)} path Path to array. -* @return {*} -*/ -Polymer_PropertyEffects.prototype.pop = function(path){}; -/** -* @override -* @param {(string | !Array.<(string | number)>)} path Path to array. -* @param {number} start Index from which to start removing/inserting. -* @param {number=} deleteCount Number of items to remove. -* @param {...*} items Items to insert into array. -* @return {Array} -*/ -Polymer_PropertyEffects.prototype.splice = function(path, start, deleteCount, items){}; -/** -* @override -* @param {(string | !Array.<(string | number)>)} path Path to array. -* @return {*} -*/ -Polymer_PropertyEffects.prototype.shift = function(path){}; -/** -* @override -* @param {(string | !Array.<(string | number)>)} path Path to array. -* @param {...*} items Items to insert info array -* @return {number} -*/ -Polymer_PropertyEffects.prototype.unshift = function(path, items){}; -/** -* @override -* @param {string} path Path that should be notified. -* @param {*=} value Value at the path (optional). -* @return {void} -*/ -Polymer_PropertyEffects.prototype.notifyPath = function(path, value){}; -/** -* @override -* @param {string} property Property name -* @param {boolean=} protectedSetter Creates a custom protected setter - when `true`. -* @return {void} -*/ -Polymer_PropertyEffects.prototype._createReadOnlyProperty = function(property, protectedSetter){}; -/** -* @override -* @param {string} property Property name -* @param {(string | function (*, *))} method Function or name of observer method - to call -* @param {boolean=} dynamicFn Whether the method name should be included as - a dependency to the effect. -* @return {void} -*/ -Polymer_PropertyEffects.prototype._createPropertyObserver = function(property, method, dynamicFn){}; -/** -* @override -* @param {string} expression Method expression -* @param {(boolean | Object)=} dynamicFn Boolean or object map indicating - whether method names should be included as a dependency to the effect. -* @return {void} -*/ -Polymer_PropertyEffects.prototype._createMethodObserver = function(expression, dynamicFn){}; -/** -* @override -* @param {string} property Property name -* @return {void} -*/ -Polymer_PropertyEffects.prototype._createNotifyingProperty = function(property){}; -/** -* @override -* @param {string} property Property name -* @return {void} -*/ -Polymer_PropertyEffects.prototype._createReflectedProperty = function(property){}; -/** -* @override -* @param {string} property Name of computed property to set -* @param {string} expression Method expression -* @param {(boolean | Object)=} dynamicFn Boolean or object map indicating - whether method names should be included as a dependency to the effect. -* @return {void} -*/ -Polymer_PropertyEffects.prototype._createComputedProperty = function(property, expression, dynamicFn){}; -/** -* @override -* @param {!HTMLTemplateElement} template Template containing binding - bindings -* @param {boolean=} instanceBinding When false (default), performs - "prototypical" binding of the template and overwrites any previously - bound template for the class. When true (as passed from - `_stampTemplate`), the template info is instanced and linked into - the list of bound templates. -* @return {!TemplateInfo} -*/ -Polymer_PropertyEffects.prototype._bindTemplate = function(template, instanceBinding){}; -/** -* @override -* @param {!StampedTemplate} dom DocumentFragment previously returned - from `_stampTemplate` associated with the nodes to be removed -* @return {void} -*/ -Polymer_PropertyEffects.prototype._removeBoundDom = function(dom){}; -/** -* @param {Node} node Node to parse -* @param {TemplateInfo} templateInfo Template metadata for current template -* @param {NodeInfo} nodeInfo Node metadata for current template node -* @return {boolean} -*/ -Polymer_PropertyEffects._parseTemplateNode = function(node, templateInfo, nodeInfo){}; -/** -* @param {Node} node Node to parse -* @param {TemplateInfo} templateInfo Template metadata for current template -* @param {NodeInfo} nodeInfo Node metadata for current template node -* @return {boolean} -*/ -Polymer_PropertyEffects._parseTemplateNestedTemplate = function(node, templateInfo, nodeInfo){}; -/** -* @param {Element} node Node to parse -* @param {TemplateInfo} templateInfo Template metadata for current template -* @param {NodeInfo} nodeInfo Node metadata for current template node -* @param {string} name Attribute name -* @param {string} value Attribute value -* @return {boolean} -*/ -Polymer_PropertyEffects._parseTemplateNodeAttribute = function(node, templateInfo, nodeInfo, name, value){}; -/** -* @param {string} property Property that should trigger the effect -* @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES -* @param {Object=} effect Effect metadata object -* @return {void} -*/ -Polymer_PropertyEffects.addPropertyEffect = function(property, type, effect){}; -/** -* @param {string} property Property name -* @param {(string | function (*, *))} method Function or name of observer method to call -* @param {boolean=} dynamicFn Whether the method name should be included as - a dependency to the effect. -* @return {void} -*/ -Polymer_PropertyEffects.createPropertyObserver = function(property, method, dynamicFn){}; -/** -* @param {string} expression Method expression -* @param {(boolean | Object)=} dynamicFn Boolean or object map indicating -* @return {void} -*/ -Polymer_PropertyEffects.createMethodObserver = function(expression, dynamicFn){}; -/** -* @param {string} property Property name -* @return {void} -*/ -Polymer_PropertyEffects.createNotifyingProperty = function(property){}; -/** -* @param {string} property Property name -* @param {boolean=} protectedSetter Creates a custom protected setter - when `true`. -* @return {void} -*/ -Polymer_PropertyEffects.createReadOnlyProperty = function(property, protectedSetter){}; -/** -* @param {string} property Property name -* @return {void} -*/ -Polymer_PropertyEffects.createReflectedProperty = function(property){}; -/** -* @param {string} property Name of computed property to set -* @param {string} expression Method expression -* @param {(boolean | Object)=} dynamicFn Boolean or object map indicating whether - method names should be included as a dependency to the effect. -* @return {void} -*/ -Polymer_PropertyEffects.createComputedProperty = function(property, expression, dynamicFn){}; -/** -* @param {!HTMLTemplateElement} template Template containing binding - bindings -* @return {!TemplateInfo} -*/ -Polymer_PropertyEffects.bindTemplate = function(template){}; -/** -* @param {Object} templateInfo Template metadata to add effect to -* @param {string} prop Property that should trigger the effect -* @param {Object=} effect Effect metadata object -* @return {void} -*/ -Polymer_PropertyEffects._addTemplatePropertyEffect = function(templateInfo, prop, effect){}; -/** -* @param {string} text Text to parse from attribute or textContent -* @param {Object} templateInfo Current template metadata -* @return {Array.<!BindingPart>} -*/ -Polymer_PropertyEffects._parseBindings = function(text, templateInfo){}; -/** -* @param {!Polymer_PropertyEffects} inst Element that should be used as - scope for binding dependencies -* @param {BindingPart} part Binding part metadata -* @param {string} path Property/path that triggered this effect -* @param {Object} props Bag of current property changes -* @param {Object} oldProps Bag of previous values for changed properties -* @param {boolean} hasPaths True with `props` contains one or more paths -* @return {*} -*/ -Polymer_PropertyEffects._evaluateBinding = function(inst, part, path, props, oldProps, hasPaths){}; -/** -* @interface -* @extends {Polymer_PropertiesChanged} -*/ -function Polymer_PropertiesMixin(){} -/** -* @override -* @return {void} -*/ -Polymer_PropertiesMixin.prototype._initializeProperties = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertiesMixin.prototype.connectedCallback = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_PropertiesMixin.prototype.disconnectedCallback = function(){}; -/** -* @param {string} name Name of property -* @return {*} -*/ -Polymer_PropertiesMixin.typeForProperty = function(name){}; -/** -* @return {void} -*/ -Polymer_PropertiesMixin.finalize = function(){}; -/** -* @return {void} -*/ -Polymer_PropertiesMixin._finalizeClass = function(){}; -/** -* @interface -* @extends {Polymer_PropertyEffects} -* @extends {Polymer_PropertiesMixin} -*/ -function Polymer_ElementMixin(){} -/** @type {HTMLTemplateElement} */ -Polymer_ElementMixin.prototype._template; - -/** @type {string} */ -Polymer_ElementMixin.prototype._importPath; - -/** @type {string} */ -Polymer_ElementMixin.prototype.rootPath; - -/** @type {string} */ -Polymer_ElementMixin.prototype.importPath; - -/** @type {(StampedTemplate | HTMLElement | ShadowRoot)} */ -Polymer_ElementMixin.prototype.root; - -/** @type {!Object.<string, !Element>} */ -Polymer_ElementMixin.prototype.$; - -/** -* @override -* @return {void} -*/ -Polymer_ElementMixin.prototype.ready = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_ElementMixin.prototype._initializeProperties = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_ElementMixin.prototype._readyClients = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_ElementMixin.prototype.connectedCallback = function(){}; -/** -* @override -* @param {StampedTemplate} dom to attach to the element. -* @return {ShadowRoot} -*/ -Polymer_ElementMixin.prototype._attachDom = function(dom){}; -/** -* @override -* @param {Object=} properties Bag of custom property key/values to - apply to this element. -* @return {void} -*/ -Polymer_ElementMixin.prototype.updateStyles = function(properties){}; -/** -* @override -* @param {string} url URL to resolve. -* @param {string=} base Optional base URL to resolve against, defaults -to the element's `importPath` -* @return {string} -*/ -Polymer_ElementMixin.prototype.resolveUrl = function(url, base){}; -/** -* @param {!HTMLTemplateElement} template Template -* @param {!TemplateInfo} templateInfo Template metadata for current template -* @param {!NodeInfo} nodeInfo Node metadata for current template. -* @return {boolean} -*/ -Polymer_ElementMixin._parseTemplateContent = function(template, templateInfo, nodeInfo){}; -/** -* @param {!Object} props . -* @return {void} -*/ -Polymer_ElementMixin.createProperties = function(props){}; -/** -* @param {Object} templateInfo Template metadata to add effect to -* @param {string} prop Property that should trigger the effect -* @param {Object=} effect Effect metadata object -* @return {void} -*/ -Polymer_ElementMixin._addTemplatePropertyEffect = function(templateInfo, prop, effect){}; -/** -* @return {void} -*/ -Polymer_ElementMixin._finalizeClass = function(){}; -/** -* @return {void} -*/ -Polymer_ElementMixin._prepareTemplate = function(){}; -/** -* @param {Object} observers Array of observer descriptors for - this class -* @param {Object} dynamicFns Object containing keys for any properties - that are functions and should trigger the effect when the function - reference is changed -* @return {void} -*/ -Polymer_ElementMixin.createObservers = function(observers, dynamicFns){}; -/** -* @param {string} cssText Text containing styling to process -* @param {string} baseURI Base URI to rebase CSS paths against -* @return {string} -*/ -Polymer_ElementMixin._processStyleText = function(cssText, baseURI){}; -/** -* @param {string} is Tag name (or type extension name) for this element -* @return {void} -*/ -Polymer_ElementMixin._finalizeTemplate = function(is){}; -/** -* @interface -*/ -function Polymer_GestureEventListeners(){} -/** -* @override -* @param {!EventTarget} node Node to add event listener to -* @param {string} eventName Name of event -* @param {function (!Event): void} handler Listener function to add -* @return {void} -*/ -Polymer_GestureEventListeners.prototype._addEventListenerToNode = function(node, eventName, handler){}; -/** -* @override -* @param {!EventTarget} node Node to remove event listener from -* @param {string} eventName Name of event -* @param {function (!Event): void} handler Listener function to remove -* @return {void} -*/ -Polymer_GestureEventListeners.prototype._removeEventListenerFromNode = function(node, eventName, handler){}; -/** -* @interface -* @extends {Polymer_PropertyAccessors} -*/ -function Polymer_DirMixin(){} -/** @type {boolean} */ -Polymer_DirMixin.prototype.__autoDirOptOut; - -/** -* @override -* @return {void} -*/ -Polymer_DirMixin.prototype.ready = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_DirMixin.prototype.connectedCallback = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_DirMixin.prototype.disconnectedCallback = function(){}; -/** -* @param {string} cssText . -* @param {string} baseURI . -* @return {string} -*/ -Polymer_DirMixin._processStyleText = function(cssText, baseURI){}; -/** -* @param {string} text CSS text to replace DIR -* @return {string} -*/ -Polymer_DirMixin._replaceDirInCssText = function(text){}; -/** -* @interface -* @extends {Polymer_ElementMixin} -* @extends {Polymer_GestureEventListeners} -*/ -function Polymer_LegacyElementMixin(){} -/** @type {boolean} */ -Polymer_LegacyElementMixin.prototype.isAttached; - -/** @type {?WeakMap.<!Element, !Object.<string, !Function>>} */ -Polymer_LegacyElementMixin.prototype.__boundListeners; - -/** @type {?Object.<string, ?Function>} */ -Polymer_LegacyElementMixin.prototype._debouncers; - -/** @type {undefined} */ -Polymer_LegacyElementMixin.prototype.domHost; - -/** @type {string} */ -Polymer_LegacyElementMixin.prototype.is; - -/** -* @override -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.ready = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype._initializeProperties = function(){}; -/** -* @override -* @param {string} name Name of attribute. -* @param {?string} old Old value of attribute. -* @param {?string} value Current value of attribute. -* @param {?string} namespace Attribute namespace. -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.attributeChangedCallback = function(name, old, value, namespace){}; -/** -* @override -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.connectedCallback = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.disconnectedCallback = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.created = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.attached = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.detached = function(){}; -/** -* @override -* @param {string} name Name of attribute. -* @param {?string} old Old value of attribute. -* @param {?string} value Current value of attribute. -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.attributeChanged = function(name, old, value){}; -/** -* @override -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype._registered = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype._ensureAttributes = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype._applyListeners = function(){}; -/** -* @override -* @param {*} value Value to deserialize -* @return {(string | undefined)} -*/ -Polymer_LegacyElementMixin.prototype.serialize = function(value){}; -/** -* @override -* @param {string} value String to deserialize -* @param {*} type Type to deserialize the string to -* @return {*} -*/ -Polymer_LegacyElementMixin.prototype.deserialize = function(value, type){}; -/** -* @override -* @param {string} property Property name to reflect. -* @param {string=} attribute Attribute name to reflect. -* @param {*=} value Property value to reflect. -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.reflectPropertyToAttribute = function(property, attribute, value){}; -/** -* @override -* @param {*} value Value to serialize. -* @param {string} attribute Attribute name to serialize to. -* @param {Element} node Element to set attribute to. -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.serializeValueToAttribute = function(value, attribute, node){}; -/** -* @override -* @param {Object} prototype Target object to copy properties to. -* @param {Object} api Source object to copy properties from. -* @return {Object} -*/ -Polymer_LegacyElementMixin.prototype.extend = function(prototype, api){}; -/** -* @override -* @param {!Object} target Target object to copy properties to. -* @param {!Object} source Source object to copy properties from. -* @return {!Object} -*/ -Polymer_LegacyElementMixin.prototype.mixin = function(target, source){}; -/** -* @override -* @param {Object} object The object on which to set the prototype. -* @param {Object} prototype The prototype that will be set on the given -`object`. -* @return {Object} -*/ -Polymer_LegacyElementMixin.prototype.chainObject = function(object, prototype){}; -/** -* @override -* @param {HTMLTemplateElement} template HTML template element to instance. -* @return {!DocumentFragment} -*/ -Polymer_LegacyElementMixin.prototype.instanceTemplate = function(template){}; -/** -* @override -* @param {string} type Name of event type. -* @param {*=} detail Detail value containing event-specific - payload. -* @param {{bubbles: (boolean | undefined), cancelable: (boolean | undefined), composed: (boolean | undefined)}=} options Object specifying options. These may include: - `bubbles` (boolean, defaults to `true`), - `cancelable` (boolean, defaults to false), and - `node` on which to fire the event (HTMLElement, defaults to `this`). -* @return {!Event} -*/ -Polymer_LegacyElementMixin.prototype.fire = function(type, detail, options){}; -/** -* @override -* @param {?EventTarget} node Element to add event listener to. -* @param {string} eventName Name of event to listen for. -* @param {string} methodName Name of handler method on `this` to call. -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.listen = function(node, eventName, methodName){}; -/** -* @override -* @param {?EventTarget} node Element to remove event listener from. -* @param {string} eventName Name of event to stop listening to. -* @param {string} methodName Name of handler method on `this` to not call - anymore. -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.unlisten = function(node, eventName, methodName){}; -/** -* @override -* @param {string=} direction Direction to allow scrolling -Defaults to `all`. -* @param {Element=} node Element to apply scroll direction setting. -Defaults to `this`. -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.setScrollDirection = function(direction, node){}; -/** -* @override -* @param {string} slctr Selector to run on this local DOM scope -* @return {Element} -*/ -Polymer_LegacyElementMixin.prototype.$$ = function(slctr){}; -/** -* @override -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.distributeContent = function(){}; -/** -* @override -* @return {!Array.<!Node>} -*/ -Polymer_LegacyElementMixin.prototype.getEffectiveChildNodes = function(){}; -/** -* @override -* @param {string} selector Selector to run. -* @return {!Array.<!Node>} -*/ -Polymer_LegacyElementMixin.prototype.queryDistributedElements = function(selector){}; -/** -* @override -* @return {!Array.<!Node>} -*/ -Polymer_LegacyElementMixin.prototype.getEffectiveChildren = function(){}; -/** -* @override -* @return {string} -*/ -Polymer_LegacyElementMixin.prototype.getEffectiveTextContent = function(){}; -/** -* @override -* @param {string} selector Selector to run. -* @return {Node} -*/ -Polymer_LegacyElementMixin.prototype.queryEffectiveChildren = function(selector){}; -/** -* @override -* @param {string} selector Selector to run. -* @return {!Array.<!Node>} -*/ -Polymer_LegacyElementMixin.prototype.queryAllEffectiveChildren = function(selector){}; -/** -* @override -* @param {string=} slctr CSS selector to choose the desired - `<slot>`. Defaults to `content`. -* @return {!Array.<!Node>} -*/ -Polymer_LegacyElementMixin.prototype.getContentChildNodes = function(slctr){}; -/** -* @override -* @param {string=} slctr CSS selector to choose the desired - `<content>`. Defaults to `content`. -* @return {!Array.<!HTMLElement>} -*/ -Polymer_LegacyElementMixin.prototype.getContentChildren = function(slctr){}; -/** -* @override -* @param {?Node} node The element to be checked. -* @return {boolean} -*/ -Polymer_LegacyElementMixin.prototype.isLightDescendant = function(node){}; -/** -* @override -* @param {!Element} node The element to be checked. -* @return {boolean} -*/ -Polymer_LegacyElementMixin.prototype.isLocalDescendant = function(node){}; -/** -* @override -* @param {*} container Unused -* @param {*} shouldObserve Unused -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.scopeSubtree = function(container, shouldObserve){}; -/** -* @override -* @param {string} property The css property name. -* @return {string} -*/ -Polymer_LegacyElementMixin.prototype.getComputedStyleValue = function(property){}; -/** -* @override -* @param {string} jobName String to identify the debounce job. -* @param {function (): void} callback Function that is called (with `this` - context) when the wait time elapses. -* @param {number=} wait Optional wait time in milliseconds (ms) after the - last signal that must elapse before invoking `callback` -* @return {!Object} -*/ -Polymer_LegacyElementMixin.prototype.debounce = function(jobName, callback, wait){}; -/** -* @override -* @param {string} jobName The name of the debouncer started with `debounce` -* @return {boolean} -*/ -Polymer_LegacyElementMixin.prototype.isDebouncerActive = function(jobName){}; -/** -* @override -* @param {string} jobName The name of the debouncer started with `debounce` -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.flushDebouncer = function(jobName){}; -/** -* @override -* @param {string} jobName The name of the debouncer started with `debounce` -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.cancelDebouncer = function(jobName){}; -/** -* @override -* @param {!Function} callback The callback function to run, bound to - `this`. -* @param {number=} waitTime Time to wait before calling the - `callback`. If unspecified or 0, the callback will be run at microtask - timing (before paint). -* @return {number} -*/ -Polymer_LegacyElementMixin.prototype.async = function(callback, waitTime){}; -/** -* @override -* @param {number} handle Handle returned from original `async` call to - cancel. -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.cancelAsync = function(handle){}; -/** -* @override -* @param {string} tag HTML element tag to create. -* @param {Object=} props Object of properties to configure on the - instance. -* @return {!Element} -*/ -Polymer_LegacyElementMixin.prototype.create = function(tag, props){}; -/** -* @override -* @param {string} selector Selector to test. -* @param {!Element=} node Element to test the selector against. -* @return {boolean} -*/ -Polymer_LegacyElementMixin.prototype.elementMatches = function(selector, node){}; -/** -* @override -* @param {string} name HTML attribute name -* @param {boolean=} bool Boolean to force the attribute on or off. - When unspecified, the state of the attribute will be reversed. -* @return {boolean} -*/ -Polymer_LegacyElementMixin.prototype.toggleAttribute = function(name, bool){}; -/** -* @override -* @param {string} name CSS class name -* @param {boolean=} bool Boolean to force the class on or off. - When unspecified, the state of the class will be reversed. -* @param {Element=} node Node to target. Defaults to `this`. -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.toggleClass = function(name, bool, node){}; -/** -* @override -* @param {string} transformText Transform setting. -* @param {Element=} node Element to apply the transform to. -Defaults to `this` -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.transform = function(transformText, node){}; -/** -* @override -* @param {number} x X offset. -* @param {number} y Y offset. -* @param {number} z Z offset. -* @param {Element=} node Element to apply the transform to. -Defaults to `this`. -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype.translate3d = function(x, y, z, node){}; -/** -* @override -* @param {(string | !Array.<(number | string)>)} arrayOrPath Path to array from - which to remove the item - (or the array itself). -* @param {*} item Item to remove. -* @return {Array} -*/ -Polymer_LegacyElementMixin.prototype.arrayDelete = function(arrayOrPath, item){}; -/** -* @override -* @param {string} level One of 'log', 'warn', 'error' -* @param {Array} args Array of strings or objects to log -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype._logger = function(level, args){}; -/** -* @override -* @param {...*} args Array of strings or objects to log -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype._log = function(args){}; -/** -* @override -* @param {...*} args Array of strings or objects to log -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype._warn = function(args){}; -/** -* @override -* @param {...*} args Array of strings or objects to log -* @return {void} -*/ -Polymer_LegacyElementMixin.prototype._error = function(args){}; -/** -* @override -* @param {string} methodName Method name to associate with message -* @param {...*} args Array of strings or objects to log -* @return {Array} -*/ -Polymer_LegacyElementMixin.prototype._logf = function(methodName, args){}; -/** -* @interface -*/ -function Polymer_MutableData(){} -/** -* @param {string} property Property name -* @param {*} value New property value -* @param {*} old Previous property value -* @return {boolean} -*/ -Polymer_MutableData.prototype._shouldPropertyChange = function(property, value, old){}; -/** -* @interface -*/ -function Polymer_OptionalMutableData(){} -/** @type {boolean | null | undefined} */ -Polymer_OptionalMutableData.prototype.mutableData; - -/** -* @param {string} property Property name -* @param {*} value New property value -* @param {*} old Previous property value -* @return {boolean} -*/ -Polymer_OptionalMutableData.prototype._shouldPropertyChange = function(property, value, old){}; -/** -* @interface -* @extends {Polymer_ElementMixin} -*/ -function Polymer_ArraySelectorMixin(){} -/** @type {Array | null | undefined} */ -Polymer_ArraySelectorMixin.prototype.items; - -/** @type {boolean | null | undefined} */ -Polymer_ArraySelectorMixin.prototype.multi; - -/** @type {(?Object | ?Array.<!Object>)} */ -Polymer_ArraySelectorMixin.prototype.selected; - -/** @type {?Object} */ -Polymer_ArraySelectorMixin.prototype.selectedItem; - -/** @type {boolean | null | undefined} */ -Polymer_ArraySelectorMixin.prototype.toggle; - -/** -* @override -* @return {void} -*/ -Polymer_ArraySelectorMixin.prototype.clearSelection = function(){}; -/** -* @override -* @param {*} item Item from `items` array to test -* @return {boolean} -*/ -Polymer_ArraySelectorMixin.prototype.isSelected = function(item){}; -/** -* @override -* @param {number} idx Index from `items` array to test -* @return {boolean} -*/ -Polymer_ArraySelectorMixin.prototype.isIndexSelected = function(idx){}; -/** -* @override -* @param {*} item Item from `items` array to deselect -* @return {void} -*/ -Polymer_ArraySelectorMixin.prototype.deselect = function(item){}; -/** -* @override -* @param {number} idx Index from `items` array to deselect -* @return {void} -*/ -Polymer_ArraySelectorMixin.prototype.deselectIndex = function(idx){}; -/** -* @override -* @param {*} item Item from `items` array to select -* @return {void} -*/ -Polymer_ArraySelectorMixin.prototype.select = function(item){}; -/** -* @override -* @param {number} idx Index from `items` array to select -* @return {void} -*/ -Polymer_ArraySelectorMixin.prototype.selectIndex = function(idx){}; -/** -* @interface -* @extends {Polymer_PropertyEffects} -*/ -function Polymer_StrictBindingParser(){} -/** -* @param {string} text Text to parse from attribute or textContent -* @param {Object} templateInfo Current template metadata -* @return {Array.<!BindingPart>} -*/ -Polymer_StrictBindingParser._parseBindings = function(text, templateInfo){}; -/** -* @interface -* @extends {Polymer_ElementMixin} -*/ -function Polymer_DisableUpgradeMixin(){} -/** -* @override -* @return {void} -*/ -Polymer_DisableUpgradeMixin.prototype._initializeProperties = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_DisableUpgradeMixin.prototype._enableProperties = function(){}; -/** -* @override -* @param {string} name Attribute name. -* @param {?string} old The previous value for the attribute. -* @param {?string} value The new value for the attribute. -* @param {?string=} namespace The XML namespace for the attribute. -* @return {undefined} -*/ -Polymer_DisableUpgradeMixin.prototype.attributeChangedCallback = function(name, old, value, namespace){}; -/** -* @override -* @return {void} -*/ -Polymer_DisableUpgradeMixin.prototype.connectedCallback = function(){}; -/** -* @override -* @return {void} -*/ -Polymer_DisableUpgradeMixin.prototype.disconnectedCallback = function(){}; -/** -* @interface -*/ -function Polymer_LegacyDataMixin(){} -/** -* @interface -*/ -function Polymer_TemplatizeMixin(){} \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-dom-api-externs.js b/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-dom-api-externs.js deleted file mode 100644 index 9b52e339..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-dom-api-externs.js +++ /dev/null
@@ -1,179 +0,0 @@ -/** - * @externs - * @fileoverview Externs for PolymerDomApi for backwards compatibility with - * the Polymer 1 externs. - */ - -/** - * A Polymer DOM API for manipulating DOM such that local DOM and light DOM - * trees are properly maintained. - * - * This type exists only to provide compatibility between compiled hybrid - * Polymer V1 and V2 code. Polymer V2 only code should simply use the DomApi - * class type. - * - * @interface - */ -var PolymerDomApi = function() {}; - -/** - * @param {?Node} node - * @return {boolean} - */ -PolymerDomApi.prototype.deepContains = function(node) {}; - -/** @param {!Node} node */ -PolymerDomApi.prototype.appendChild = function(node) {}; - -/** - * @param {!Node} oldNode - * @param {!Node} newNode - */ -PolymerDomApi.prototype.replaceChild = function(oldNode, newNode) {}; - -/** - * @param {!Node} node - * @param {?Node} beforeNode - */ -PolymerDomApi.prototype.insertBefore = function(node, beforeNode) {}; - -/** @param {!Node} node */ -PolymerDomApi.prototype.removeChild = function(node) {}; - -/** @type {!Array<!HTMLElement>|!NodeList<!HTMLElement>} */ -PolymerDomApi.prototype.children; - -/** @type {!Array<!Node>|!NodeList<!Node>} */ -PolymerDomApi.prototype.childNodes; - -/** @type {?Node} */ -PolymerDomApi.prototype.parentNode; - -/** @type {?Node} */ -PolymerDomApi.prototype.firstChild; - -/** @type {?Node} */ -PolymerDomApi.prototype.lastChild; - -/** @type {?HTMLElement} */ -PolymerDomApi.prototype.firstElementChild; - -/** @type {?HTMLElement} */ -PolymerDomApi.prototype.lastElementChild; - -/** @type {?Node} */ -PolymerDomApi.prototype.previousSibling; - -/** @type {?Node} */ -PolymerDomApi.prototype.nextSibling; - -/** @type {?HTMLElement} */ -PolymerDomApi.prototype.previousElementSibling; - -/** @type {?HTMLElement} */ -PolymerDomApi.prototype.nextElementSibling; - -/** @type {string} */ -PolymerDomApi.prototype.textContent; - -/** @type {string} */ -PolymerDomApi.prototype.innerHTML; - -/** @type {?HTMLElement} */ -PolymerDomApi.prototype.activeElement; - -/** - * @param {string} selector - * @return {?Element} - */ -PolymerDomApi.prototype.querySelector = function(selector) {}; - -/** - * @param {string} selector - * @return {!Array<!Element>|!NodeList<!Element>} - */ -PolymerDomApi.prototype.querySelectorAll = function(selector) {}; - -/** @return {!Array<!Node>} */ -PolymerDomApi.prototype.getDistributedNodes = function() {}; - -/** @return {!Array<!Node>} */ -PolymerDomApi.prototype.getDestinationInsertionPoints = function() {}; - -/** @return {?Node} */ -PolymerDomApi.prototype.getOwnerRoot = function() {}; - -/** - * @param {string} attribute - * @param {string} value - */ -PolymerDomApi.prototype.setAttribute = function(attribute, value) {}; - -/** @param {string} attribute */ -PolymerDomApi.prototype.removeAttribute = function(attribute) {}; - -/** - * @typedef {function(!PolymerDomApi.ObserveInfo)} - */ -PolymerDomApi.ObserveCallback; - -/** - * @typedef {{ - * target: !Node, - * addedNodes: !Array<!Node>, - * removedNodes: !Array<!Node> - * }} - */ -PolymerDomApi.ObserveInfo; - -/** - * A virtual type for observer callback handles. - * - * @interface - */ -PolymerDomApi.ObserveHandle = function() {}; - -/** - * @return {void} - */ -PolymerDomApi.ObserveHandle.prototype.disconnect = function() {}; - -/** - * Notifies callers about changes to the element's effective child nodes, - * the same list as returned by `getEffectiveChildNodes`. - * - * @param {!PolymerDomApi.ObserveCallback} callback The supplied callback - * is called with an `info` argument which is an object that provides - * the `target` on which the changes occurred, a list of any nodes - * added in the `addedNodes` array, and nodes removed in the - * `removedNodes` array. - * - * @return {!PolymerDomApi.ObserveHandle} Handle which is the argument to - * `unobserveNodes`. - */ -PolymerDomApi.prototype.observeNodes = function(callback) {}; - -/** - * Stops observing changes to the element's effective child nodes. - * - * @param {!PolymerDomApi.ObserveHandle} handle The handle for the - * callback that should no longer receive notifications. This - * handle is returned from `observeNodes`. - */ -PolymerDomApi.prototype.unobserveNodes = function(handle) {}; - -/** @type {?DOMTokenList} */ -PolymerDomApi.prototype.classList; - -/** - * @param {string} selector - * @return {!Array<!HTMLElement>} - */ -PolymerDomApi.prototype.queryDistributedElements = function(selector) {}; - -/** - * Returns a list of effective child nodes for this element. - * - * @return {!Array<!HTMLElement>} - */ -PolymerDomApi.prototype.getEffectiveChildNodes = function() {};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-externs.js b/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-externs.js deleted file mode 100644 index 0c7b8ea4..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-externs.js +++ /dev/null
@@ -1,216 +0,0 @@ -/** - * @fileoverview Externs for Polymer Pass and external Polymer API - * @externs - * - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt - */ - -/* eslint-disable */ - -/** - * @typedef {{ - * type: !Function, - * value: (* | undefined), - * readOnly: (boolean | undefined), - * computed: (string | undefined), - * reflectToAttribute: (boolean | undefined), - * notify: (boolean | undefined), - * observer: (string | function(this:?, ?, ?) | undefined) - * }} - */ -let PolymerElementPropertiesMeta; - -/** - * @typedef {Object<string, !Function|!PolymerElementPropertiesMeta>} - */ -let PolymerElementProperties; - -/** @record */ -let PolymerInit = function() {}; -/** @type {string} */ -PolymerInit.prototype.is; -/** @type {(string | undefined)} */ -PolymerInit.prototype.extends; -/** @type {(!PolymerElementProperties | undefined)} */ -PolymerInit.prototype.properties; -/** @type {(!Array<string> | undefined)} */ -PolymerInit.prototype.observers; -/** @type {(!HTMLTemplateElement | string | undefined)} */ -PolymerInit.prototype.template; -/** @type {(!Object<string, *> | undefined)} */ -PolymerInit.prototype.hostAttributes; -/** @type {(!Object<string, string> | undefined)} */ -PolymerInit.prototype.listeners; -/** @type {(!Object| !Array<!Object> | undefined)} */ -PolymerInit.prototype.behaviors; - -/** @record */ -let PolymerElementConstructor = function () {}; -/** @type {(string | undefined)} */ -PolymerElementConstructor.is; -/** @type {(string | undefined)} */ -PolymerElementConstructor.extends; -/** @type {(!PolymerElementProperties | undefined)} */ -PolymerElementConstructor.properties; -/** @type {(!Array<string> | undefined)} */ -PolymerElementConstructor.observers; -/** @type {(!HTMLTemplateElement | string | undefined)} */ -PolymerElementConstructor.template; - -/** @interface */ -let PropertiesMixinConstructor = function () {}; -/** @type {(!PolymerElementProperties | undefined)} */ -PropertiesMixinConstructor.prototype.properties; -/** @return {void} */ -PropertiesMixinConstructor.prototype.finalize = function() {}; - -/** - * @param {!PolymerInit} init - * @return {!function(new:HTMLElement)} - */ -function Polymer(init){} - -/** - * @type {(function(*,string,string,Node):*)|undefined} - */ -Polymer.sanitizeDOMValue; - -/** - * @type {boolean} - */ -Polymer.passiveTouchGestures; - -/** - * @type {boolean} - */ -Polymer.strictTemplatePolicy; - -/** - * @type {boolean} - */ -Polymer.allowTemplateFromDomModule; - -/** - * @type {string} - */ -Polymer.rootPath; - -/** - * @param {string} string - * @param {Object} obj - * @return {string} - */ -function JSCompiler_renameProperty(string, obj) {} - -/** @record */ -function PolymerTelemetry() {} -/** @type {number} */ -PolymerTelemetry.instanceCount; -/** @type {function():void} */ -PolymerTelemetry.incrementInstanceCount; -/** @type {Array<HTMLElement>} */ -PolymerTelemetry.registrations; -/** @type {function(HTMLElement)} */ -PolymerTelemetry._regLog; -/** @type {function(HTMLElement)} */ -PolymerTelemetry.register; -/** @type {function(HTMLElement)} */ -PolymerTelemetry.dumpRegistrations; - -/** @type {PolymerTelemetry} */ -Polymer.telemetry; - -/** @type {string} */ -Polymer.version; - -/** @type {boolean} */ -Polymer.legacyOptimizations; - -/** @type {boolean} */ -Polymer.syncInitialRender; - -// nb. This is explicitly 'var', as Closure Compiler checks that this is the case. -/** - * @constructor - * @extends {HTMLElement} - * @implements {Polymer_LegacyElementMixin} - */ -var PolymerElement = function() {}; - -/** - * On create callback. - * @override - */ -PolymerElement.prototype.created = function() {}; -/** - * On ready callback. - * @override - */ -PolymerElement.prototype.ready = function() {}; -/** On before register callback. */ -PolymerElement.prototype.beforeRegister = function() {}; -/** On registered callback. */ -PolymerElement.prototype.registered = function() {}; -/** - * On attached to the DOM callback. - * @override - */ -PolymerElement.prototype.attached = function() {}; -/** - * On detached from the DOM callback. - * @override - */ -PolymerElement.prototype.detached = function() {}; - -/** - * @typedef {{ - * index: number, - * removed: !Array, - * addedCount: number, - * object: !Array, - * type: string, - * }} - */ -var PolymerSplice; -/** - * @typedef {{ - * indexSplices: ?Array<!PolymerSplice>, - * }} - */ -var PolymerSpliceChange; - -/** - * The type of the object received by an observer function when deep - * sub-property observation is enabled. See: - * https://www.polymer-project.org/2.0/docs/devguide/observers#deep-observation - * - * @typedef {{ - * path: string, - * value: (?Object|undefined), - * base: (?Object|undefined) - * }} - */ -var PolymerDeepPropertyChange; - -/** - * Event object for events dispatched by children of a dom-repeat template. - * @see https://www.polymer-project.org/2.0/docs/devguide/templates#handling-events - * @extends {Event} - * @constructor - * @template T - */ -var DomRepeatEvent = function() {}; - -/** - * @type {{ - * index: number, - * item: T - * }} - */ -DomRepeatEvent.prototype.model;
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-iconset-externs.js b/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-iconset-externs.js deleted file mode 100644 index 916fee8..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-iconset-externs.js +++ /dev/null
@@ -1,36 +0,0 @@ -/** - * @externs - * @fileoverview Externs for Polymer.Iconset. - */ - -/** - * The interface that iconsets should obey. Iconsets are registered by setting - * their name in the IronMeta 'iconset' db, and a value of type Polymer.Iconset. - * - * Used by iron-icon but needs to live here since iron-icon, iron-iconset, etc don't - * depend on each other at all and talk only through iron-meta. - * - * @interface - */ -Polymer.Iconset = function() {}; - -/** - * Applies an icon to the given element as a css background image. This - * method does not size the element, and it's usually necessary to set - * the element's height and width so that the background image is visible. - * - * @param {Element} element The element to which the icon is applied. - * @param {string} icon The name of the icon to apply. - * @param {string=} theme (optional) The name or index of the icon to apply. - * @param {number=} scale (optional, defaults to 1) Icon scaling factor. - */ -Polymer.Iconset.prototype.applyIcon = function( - element, icon, theme, scale) {}; - -/** - * Remove an icon from the given element by undoing the changes effected - * by `applyIcon`. - * - * @param {Element} element The element from which the icon is removed. - */ -Polymer.Iconset.prototype.removeIcon = function(element) {};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-shared-types.js b/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-shared-types.js deleted file mode 100644 index fd954c11..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-shared-types.js +++ /dev/null
@@ -1,214 +0,0 @@ -/** - * @fileoverview Internal shared types for Polymer - * @externs - * - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt - */ - -/* eslint-disable no-unused-vars, strict, valid-jsdoc */ - -/** - * @constructor - * @extends {DocumentFragment} - */ -function StampedTemplate() { } -/** @type {boolean} */ -StampedTemplate.prototype.__noInsertionPoint; -/** @type {!Array<!Node>} */ -StampedTemplate.prototype.nodeList; -/** @type {!Object<string, !Element>} */ -StampedTemplate.prototype.$; -/** @type {!TemplateInfo | undefined} */ -StampedTemplate.prototype.templateInfo; - -/** @interface */ -function NodeInfo() { } -/** @type {string} */ -NodeInfo.prototype.id; -/** @type {!Array<{name: string, value: string}>}*/ -NodeInfo.prototype.events; -/** @type {boolean} */ -NodeInfo.prototype.hasInsertionPoint; -/** @type {!TemplateInfo} */ -NodeInfo.prototype.templateInfo; -/** @type {!NodeInfo} */ -NodeInfo.prototype.parentInfo; -/** @type {number} */ -NodeInfo.prototype.parentIndex; -/** @type {number} */ -NodeInfo.prototype.infoIndex; -/** @type {!Array<!Binding>} */ -NodeInfo.prototype.bindings; - -/** @interface */ -function TemplateInfo() { } -/** @type {!Array<!NodeInfo>} */ -TemplateInfo.prototype.nodeInfoList; -/** @type {!Array<!Node>} */ -TemplateInfo.prototype.nodeList; -/** @type {boolean} */ -TemplateInfo.prototype.stripWhitespace; -/** @type {boolean | undefined} */ -TemplateInfo.prototype.hasInsertionPoint; -/** @type {!Object} */ -TemplateInfo.prototype.hostProps; -/** @type {!Object} */ -TemplateInfo.prototype.propertyEffects; -/** @type {TemplateInfo | undefined} */ -TemplateInfo.prototype.nextTemplateInfo; -/** @type {TemplateInfo | undefined} */ -TemplateInfo.prototype.previousTemplateInfo; -/** @type {!Array<!Node>} */ -TemplateInfo.prototype.childNodes; -/** @type {boolean} */ -TemplateInfo.prototype.wasPreBound; - -/** - * type for HTMLTemplateElement with `_templateInfo` - * - * @constructor - * @extends {HTMLTemplateElement} - */ -function HTMLTemplateElementWithInfo() { } -/** @type {TemplateInfo} */ -HTMLTemplateElementWithInfo.prototype._templateInfo; - -/** - * @typedef {{ - * literal: string, - * compoundIndex: (number | undefined) - * }} - */ -let LiteralBindingPart; - -/** - * @typedef {{ - * literal: boolean, - * name: string, - * value: (string | number), - * rootProperty: (string | undefined), - * structured: (boolean | undefined), - * wildcard: (boolean | undefined) - * }} - */ -let MethodArg; - -/** - * @typedef {{ - * methodName: string, - * static: boolean, - * args: !Array<!MethodArg>, - * dynamicFn: (boolean | undefined), - * }} - */ -let MethodSignature; - -/** - * @typedef {{ - * mode: string, - * negate: boolean, - * source: string, - * dependencies: !Array<(!MethodArg|string)>, - * customEvent: boolean, - * signature: Object, - * event: string - * }} - */ -let ExpressionBindingPart; - -/** - * @typedef {LiteralBindingPart | ExpressionBindingPart} - */ -let BindingPart; - -/** - * @typedef {{ - * kind: string, - * target: string, - * parts: Array<!BindingPart>, - * literal: (string | undefined), - * isCompound: boolean, - * listenerEvent: (string | undefined), - * listenerNegate: (boolean | undefined) - * }} - */ -let Binding; - -/** - * @typedef {{ - * path: string - * }} - */ -let PathInfo; - -/** - * @typedef {{ - * forwardHostProp: (function(string, *) | undefined), - * instanceProps: (Object | undefined), - * mutableData: (boolean | undefined), - * notifyInstanceProp: (function(*, string, *) | undefined), - * parentModel: (boolean | undefined) - * }} - */ -let TemplatizeOptions; - -/** @record */ -function AsyncInterface(){} -/** @type {function(!Function, number=): number} */ -AsyncInterface.prototype.run; -/** @type {function(number): void} */ -AsyncInterface.prototype.cancel; - -/** @record */ -let GestureInfo = function(){}; -/** @type {string|undefined} */ -GestureInfo.prototype.state; -/** @type {boolean|undefined} */ -GestureInfo.prototype.started; -/** @type {!Array<?>|undefined} */ -GestureInfo.prototype.moves; -/** @type {number|undefined} */ -GestureInfo.prototype.x; -/** @type {number|undefined} */ -GestureInfo.prototype.y; -/** @type {boolean|undefined} */ -GestureInfo.prototype.prevent; -/** @type {function(?): void|undefined} */ -GestureInfo.prototype.addMove; -/** @type {null|undefined} */ -GestureInfo.prototype.movefn; -/** @type {null|undefined} */ -GestureInfo.prototype.upFn; - -/** @record */ -let GestureRecognizer = function(){}; -/** @type {string} */ -GestureRecognizer.prototype.name; -/** @type {!Array<string>} */ -GestureRecognizer.prototype.deps; -/** @type {function(): void} */ -GestureRecognizer.prototype.reset; -/** @type {function(MouseEvent): void | undefined} */ -GestureRecognizer.prototype.mousedown; -/** @type {(function(MouseEvent): void | undefined)} */ -GestureRecognizer.prototype.mousemove; -/** @type {(function(MouseEvent): void | undefined)} */ -GestureRecognizer.prototype.mouseup; -/** @type {(function(TouchEvent): void | undefined)} */ -GestureRecognizer.prototype.touchstart; -/** @type {(function(TouchEvent): void | undefined)} */ -GestureRecognizer.prototype.touchmove; -/** @type {(function(TouchEvent): void | undefined)} */ -GestureRecognizer.prototype.touchend; -/** @type {(function(MouseEvent): void | undefined)} */ -GestureRecognizer.prototype.click; -/** @type {!GestureInfo} */ -GestureRecognizer.prototype.info; -/** @type {!Array<string>} */ -GestureRecognizer.prototype.emits;
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-types.html b/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-types.html deleted file mode 100644 index bc7abad..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-types.html +++ /dev/null
@@ -1,11 +0,0 @@ -<!-- -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt ---> -<!-- internal shared types for closure pass --> -<script src="polymer-internal-shared-types.js"></script> \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/externs/webcomponents-externs.js b/third_party/polymer/v3_0/components-chromium/polymer/externs/webcomponents-externs.js deleted file mode 100644 index 1d796de..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/externs/webcomponents-externs.js +++ /dev/null
@@ -1,68 +0,0 @@ -/** - * @fileoverview Externs for webcomponents polyfills - * @externs - * - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt - */ -/* eslint-disable */ - -var HTMLImports = { - /** - * @param {function()} callback - */ - whenReady(callback) {}, - /** - * @param {!Node} element - * @return {?HTMLLinkElement|?Document|undefined} - */ - importForElement(element) {} -}; - -window.HTMLImports = HTMLImports; - -var ShadyDOM = { - inUse: false, - flush() {}, - /** - * @param {!Node} target - * @param {function(Array<MutationRecord>, MutationObserver)} callback - * @return {MutationObserver} - */ - observeChildren(target, callback) {}, - /** - * @param {MutationObserver} observer - */ - unobserveChildren(observer) {}, - /** - * @param {Node} node - */ - patch(node) {}, - /** - * @param {!ShadowRoot} shadowroot - */ - flushInitial(shadowroot) {} -}; - -window.ShadyDOM = ShadyDOM; - -var WebComponents = {}; -window.WebComponents = WebComponents; - -/** @type {Element} */ -HTMLElement.prototype._activeElement; - -/** - * @param {HTMLTemplateElement} template - */ -HTMLTemplateElement.decorate = function(template){}; - -/** - * @param {function(function())} cb callback - */ -CustomElementRegistry.prototype.polyfillWrapFlushCallback = function(cb){};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/array-selector.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/array-selector.js deleted file mode 100644 index 158d242c..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/array-selector.js +++ /dev/null
@@ -1,434 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import { PolymerElement } from '../../polymer-element.js'; - -import { dedupingMixin } from '../utils/mixin.js'; -import { calculateSplices } from '../utils/array-splice.js'; -import { ElementMixin } from '../mixins/element-mixin.js'; - -/** - * Element mixin for recording dynamic associations between item paths in a - * master `items` array and a `selected` array such that path changes to the - * master array (at the host) element or elsewhere via data-binding) are - * correctly propagated to items in the selected array and vice-versa. - * - * The `items` property accepts an array of user data, and via the - * `select(item)` and `deselect(item)` API, updates the `selected` property - * which may be bound to other parts of the application, and any changes to - * sub-fields of `selected` item(s) will be kept in sync with items in the - * `items` array. When `multi` is false, `selected` is a property - * representing the last selected item. When `multi` is true, `selected` - * is an array of multiply selected items. - * - * @polymer - * @mixinFunction - * @appliesMixin ElementMixin - * @summary Element mixin for recording dynamic associations between item paths in a - * master `items` array and a `selected` array - */ -let ArraySelectorMixin = dedupingMixin(superClass => { - - /** - * @constructor - * @implements {Polymer_ElementMixin} - * @private - */ - let elementBase = ElementMixin(superClass); - - /** - * @polymer - * @mixinClass - * @implements {Polymer_ArraySelectorMixin} - * @unrestricted - */ - class ArraySelectorMixin extends elementBase { - - static get properties() { - return { - - /** - * An array containing items from which selection will be made. - */ - items: { - type: Array, - }, - - /** - * When `true`, multiple items may be selected at once (in this case, - * `selected` is an array of currently selected items). When `false`, - * only one item may be selected at a time. - */ - multi: { - type: Boolean, - value: false, - }, - - /** - * When `multi` is true, this is an array that contains any selected. - * When `multi` is false, this is the currently selected item, or `null` - * if no item is selected. - * @type {?Object|?Array<!Object>} - */ - selected: {type: Object, notify: true}, - - /** - * When `multi` is false, this is the currently selected item, or `null` - * if no item is selected. - * @type {?Object} - */ - selectedItem: {type: Object, notify: true}, - - /** - * When `true`, calling `select` on an item that is already selected - * will deselect the item. - */ - toggle: {type: Boolean, value: false} - - }; - } - - static get observers() { - return ['__updateSelection(multi, items.*)']; - } - - constructor() { - super(); - this.__lastItems = null; - this.__lastMulti = null; - this.__selectedMap = null; - } - - __updateSelection(multi, itemsInfo) { - let path = itemsInfo.path; - if (path == JSCompiler_renameProperty('items', this)) { - // Case 1 - items array changed, so diff against previous array and - // deselect any removed items and adjust selected indices - let newItems = itemsInfo.base || []; - let lastItems = this.__lastItems; - let lastMulti = this.__lastMulti; - if (multi !== lastMulti) { - this.clearSelection(); - } - if (lastItems) { - let splices = calculateSplices(newItems, lastItems); - this.__applySplices(splices); - } - this.__lastItems = newItems; - this.__lastMulti = multi; - } else if (itemsInfo.path == `${JSCompiler_renameProperty('items', this)}.splices`) { - // Case 2 - got specific splice information describing the array mutation: - // deselect any removed items and adjust selected indices - this.__applySplices(itemsInfo.value.indexSplices); - } else { - // Case 3 - an array element was changed, so deselect the previous - // item for that index if it was previously selected - let part = path.slice(`${JSCompiler_renameProperty('items', this)}.`.length); - let idx = parseInt(part, 10); - if ((part.indexOf('.') < 0) && part == idx) { - this.__deselectChangedIdx(idx); - } - } - } - - __applySplices(splices) { - let selected = this.__selectedMap; - // Adjust selected indices and mark removals - for (let i=0; i<splices.length; i++) { - let s = splices[i]; - selected.forEach((idx, item) => { - if (idx < s.index) { - // no change - } else if (idx >= s.index + s.removed.length) { - // adjust index - selected.set(item, idx + s.addedCount - s.removed.length); - } else { - // remove index - selected.set(item, -1); - } - }); - for (let j=0; j<s.addedCount; j++) { - let idx = s.index + j; - if (selected.has(this.items[idx])) { - selected.set(this.items[idx], idx); - } - } - } - // Update linked paths - this.__updateLinks(); - // Remove selected items that were removed from the items array - let sidx = 0; - selected.forEach((idx, item) => { - if (idx < 0) { - if (this.multi) { - this.splice(JSCompiler_renameProperty('selected', this), sidx, 1); - } else { - this.selected = this.selectedItem = null; - } - selected.delete(item); - } else { - sidx++; - } - }); - } - - __updateLinks() { - this.__dataLinkedPaths = {}; - if (this.multi) { - let sidx = 0; - this.__selectedMap.forEach(idx => { - if (idx >= 0) { - this.linkPaths( - `${JSCompiler_renameProperty('items', this)}.${idx}`, - `${JSCompiler_renameProperty('selected', this)}.${sidx++}`); - } - }); - } else { - this.__selectedMap.forEach(idx => { - this.linkPaths( - JSCompiler_renameProperty('selected', this), - `${JSCompiler_renameProperty('items', this)}.${idx}`); - this.linkPaths( - JSCompiler_renameProperty('selectedItem', this), - `${JSCompiler_renameProperty('items', this)}.${idx}`); - }); - } - } - - /** - * Clears the selection state. - * @override - * @return {void} - */ - clearSelection() { - // Unbind previous selection - this.__dataLinkedPaths = {}; - // The selected map stores 3 pieces of information: - // key: items array object - // value: items array index - // order: selected array index - this.__selectedMap = new Map(); - // Initialize selection - this.selected = this.multi ? [] : null; - this.selectedItem = null; - } - - /** - * Returns whether the item is currently selected. - * - * @override - * @param {*} item Item from `items` array to test - * @return {boolean} Whether the item is selected - */ - isSelected(item) { - return this.__selectedMap.has(item); - } - - /** - * Returns whether the item is currently selected. - * - * @override - * @param {number} idx Index from `items` array to test - * @return {boolean} Whether the item is selected - */ - isIndexSelected(idx) { - return this.isSelected(this.items[idx]); - } - - __deselectChangedIdx(idx) { - let sidx = this.__selectedIndexForItemIndex(idx); - if (sidx >= 0) { - let i = 0; - this.__selectedMap.forEach((idx, item) => { - if (sidx == i++) { - this.deselect(item); - } - }); - } - } - - __selectedIndexForItemIndex(idx) { - let selected = this.__dataLinkedPaths[`${JSCompiler_renameProperty('items', this)}.${idx}`]; - if (selected) { - return parseInt(selected.slice(`${JSCompiler_renameProperty('selected', this)}.`.length), 10); - } - } - - /** - * Deselects the given item if it is already selected. - * - * @override - * @param {*} item Item from `items` array to deselect - * @return {void} - */ - deselect(item) { - let idx = this.__selectedMap.get(item); - if (idx >= 0) { - this.__selectedMap.delete(item); - let sidx; - if (this.multi) { - sidx = this.__selectedIndexForItemIndex(idx); - } - this.__updateLinks(); - if (this.multi) { - this.splice(JSCompiler_renameProperty('selected', this), sidx, 1); - } else { - this.selected = this.selectedItem = null; - } - } - } - - /** - * Deselects the given index if it is already selected. - * - * @override - * @param {number} idx Index from `items` array to deselect - * @return {void} - */ - deselectIndex(idx) { - this.deselect(this.items[idx]); - } - - /** - * Selects the given item. When `toggle` is true, this will automatically - * deselect the item if already selected. - * - * @override - * @param {*} item Item from `items` array to select - * @return {void} - */ - select(item) { - this.selectIndex(this.items.indexOf(item)); - } - - /** - * Selects the given index. When `toggle` is true, this will automatically - * deselect the item if already selected. - * - * @override - * @param {number} idx Index from `items` array to select - * @return {void} - */ - selectIndex(idx) { - let item = this.items[idx]; - if (!this.isSelected(item)) { - if (!this.multi) { - this.__selectedMap.clear(); - } - this.__selectedMap.set(item, idx); - this.__updateLinks(); - if (this.multi) { - this.push(JSCompiler_renameProperty('selected', this), item); - } else { - this.selected = this.selectedItem = item; - } - } else if (this.toggle) { - this.deselectIndex(idx); - } - } - - } - - return ArraySelectorMixin; - -}); - -// export mixin -export { ArraySelectorMixin }; - -/** - * @constructor - * @extends {PolymerElement} - * @implements {Polymer_ArraySelectorMixin} - * @private - */ -let baseArraySelector = ArraySelectorMixin(PolymerElement); - -/** - * Element implementing the `ArraySelector` mixin, which records - * dynamic associations between item paths in a master `items` array and a - * `selected` array such that path changes to the master array (at the host) - * element or elsewhere via data-binding) are correctly propagated to items - * in the selected array and vice-versa. - * - * The `items` property accepts an array of user data, and via the - * `select(item)` and `deselect(item)` API, updates the `selected` property - * which may be bound to other parts of the application, and any changes to - * sub-fields of `selected` item(s) will be kept in sync with items in the - * `items` array. When `multi` is false, `selected` is a property - * representing the last selected item. When `multi` is true, `selected` - * is an array of multiply selected items. - * - * Example: - * - * ```js - * import {PolymerElement} from '@polymer/polymer'; - * import '@polymer/polymer/lib/elements/array-selector.js'; - * - * class EmployeeList extends PolymerElement { - * static get _template() { - * return html` - * <div> Employee list: </div> - * <dom-repeat id="employeeList" items="{{employees}}"> - * <template> - * <div>First name: <span>{{item.first}}</span></div> - * <div>Last name: <span>{{item.last}}</span></div> - * <button on-click="toggleSelection">Select</button> - * </template> - * </dom-repeat> - * - * <array-selector id="selector" - * items="{{employees}}" - * selected="{{selected}}" - * multi toggle></array-selector> - * - * <div> Selected employees: </div> - * <dom-repeat items="{{selected}}"> - * <template> - * <div>First name: <span>{{item.first}}</span></div> - * <div>Last name: <span>{{item.last}}</span></div> - * </template> - * </dom-repeat>`; - * } - * static get is() { return 'employee-list'; } - * static get properties() { - * return { - * employees: { - * value() { - * return [ - * {first: 'Bob', last: 'Smith'}, - * {first: 'Sally', last: 'Johnson'}, - * ... - * ]; - * } - * } - * }; - * } - * toggleSelection(e) { - * const item = this.$.employeeList.itemForElement(e.target); - * this.$.selector.select(item); - * } - * } - * ``` - * - * @polymer - * @customElement - * @extends {baseArraySelector} - * @appliesMixin ArraySelectorMixin - * @summary Custom element that links paths between an input `items` array and - * an output `selected` item or array based on calls to its selection API. - */ -class ArraySelector extends baseArraySelector { - // Not needed to find template; can be removed once the analyzer - // can find the tag name from customElements.define call - static get is() { return 'array-selector'; } - static get template() { return null; } -} -customElements.define(ArraySelector.is, ArraySelector); -export { ArraySelector };
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/custom-style.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/custom-style.js deleted file mode 100644 index b58b320..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/custom-style.js +++ /dev/null
@@ -1,109 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import '../../../shadycss/entrypoints/custom-style-interface.js'; - -import { cssFromModules } from '../utils/style-gather.js'; - -const attr = 'include'; - -const CustomStyleInterface = window.ShadyCSS.CustomStyleInterface; - -/** - * Custom element for defining styles in the main document that can take - * advantage of [shady DOM](https://github.com/webcomponents/shadycss) shims - * for style encapsulation, custom properties, and custom mixins. - * - * - Document styles defined in a `<custom-style>` are shimmed to ensure they - * do not leak into local DOM when running on browsers without native - * Shadow DOM. - * - Custom properties can be defined in a `<custom-style>`. Use the `html` selector - * to define custom properties that apply to all custom elements. - * - Custom mixins can be defined in a `<custom-style>`, if you import the optional - * [apply shim](https://github.com/webcomponents/shadycss#about-applyshim) - * (`shadycss/apply-shim.html`). - * - * To use: - * - * - Import `custom-style.html`. - * - Place a `<custom-style>` element in the main document, wrapping an inline `<style>` tag that - * contains the CSS rules you want to shim. - * - * For example: - * - * ```html - * <!-- import apply shim--only required if using mixins --> - * <link rel="import" href="bower_components/shadycss/apply-shim.html"> - * <!-- import custom-style element --> - * <link rel="import" href="bower_components/polymer/lib/elements/custom-style.html"> - * - * <custom-style> - * <style> - * html { - * --custom-color: blue; - * --custom-mixin: { - * font-weight: bold; - * color: red; - * }; - * } - * </style> - * </custom-style> - * ``` - * - * @customElement - * @extends HTMLElement - * @summary Custom element for defining styles in the main document that can - * take advantage of Polymer's style scoping and custom properties shims. - */ -export class CustomStyle extends HTMLElement { - constructor() { - super(); - this._style = null; - CustomStyleInterface.addCustomStyle(this); - } - /** - * Returns the light-DOM `<style>` child this element wraps. Upon first - * call any style modules referenced via the `include` attribute will be - * concatenated to this element's `<style>`. - * - * @export - * @return {HTMLStyleElement} This element's light-DOM `<style>` - */ - getStyle() { - if (this._style) { - return this._style; - } - const style = /** @type {HTMLStyleElement} */(this.querySelector('style')); - if (!style) { - return null; - } - this._style = style; - const include = style.getAttribute(attr); - if (include) { - style.removeAttribute(attr); - style.textContent = cssFromModules(include) + style.textContent; - } - /* - HTML Imports styling the main document are deprecated in Chrome - https://crbug.com/523952 - - If this element is not in the main document, then it must be in an HTML Import document. - In that case, move the custom style to the main document. - - The ordering of `<custom-style>` should stay the same as when loaded by HTML Imports, but there may be odd - cases of ordering w.r.t the main document styles. - */ - if (this.ownerDocument !== window.document) { - window.document.head.appendChild(this); - } - return this._style; - } -} - -window.customElements.define('custom-style', CustomStyle);
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-bind.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-bind.js deleted file mode 100644 index e02d041a..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-bind.js +++ /dev/null
@@ -1,143 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import '../utils/boot.js'; - -import { PropertyEffects } from '../mixins/property-effects.js'; -import { OptionalMutableData } from '../mixins/mutable-data.js'; -import { GestureEventListeners } from '../mixins/gesture-event-listeners.js'; -import { strictTemplatePolicy } from '../utils/settings.js'; -import { wrap } from '../utils/wrap.js'; - -/** - * @constructor - * @extends {HTMLElement} - * @implements {Polymer_PropertyEffects} - * @implements {Polymer_OptionalMutableData} - * @implements {Polymer_GestureEventListeners} - * @private - */ -const domBindBase = - GestureEventListeners( - OptionalMutableData( - PropertyEffects(HTMLElement))); - -/** - * Custom element to allow using Polymer's template features (data binding, - * declarative event listeners, etc.) in the main document without defining - * a new custom element. - * - * `<template>` tags utilizing bindings may be wrapped with the `<dom-bind>` - * element, which will immediately stamp the wrapped template into the main - * document and bind elements to the `dom-bind` element itself as the - * binding scope. - * - * @polymer - * @customElement - * @appliesMixin PropertyEffects - * @appliesMixin OptionalMutableData - * @appliesMixin GestureEventListeners - * @extends {domBindBase} - * @summary Custom element to allow using Polymer's template features (data - * binding, declarative event listeners, etc.) in the main document. - */ -export class DomBind extends domBindBase { - - static get observedAttributes() { return ['mutable-data']; } - - constructor() { - super(); - if (strictTemplatePolicy) { - throw new Error(`strictTemplatePolicy: dom-bind not allowed`); - } - this.root = null; - this.$ = null; - this.__children = null; - } - - /** - * @override - * @return {void} - */ - attributeChangedCallback() { - // assumes only one observed attribute - this.mutableData = true; - } - - /** - * @override - * @return {void} - */ - connectedCallback() { - this.style.display = 'none'; - this.render(); - } - - /** - * @override - * @return {void} - */ - disconnectedCallback() { - this.__removeChildren(); - } - - __insertChildren() { - wrap(wrap(this).parentNode).insertBefore(this.root, this); - } - - __removeChildren() { - if (this.__children) { - for (let i=0; i<this.__children.length; i++) { - this.root.appendChild(this.__children[i]); - } - } - } - - /** - * Forces the element to render its content. This is typically only - * necessary to call if HTMLImports with the async attribute are used. - * @return {void} - */ - render() { - let template; - if (!this.__children) { - template = /** @type {HTMLTemplateElement} */(template || this.querySelector('template')); - if (!template) { - // Wait until childList changes and template should be there by then - let observer = new MutationObserver(() => { - template = /** @type {HTMLTemplateElement} */(this.querySelector('template')); - if (template) { - observer.disconnect(); - this.render(); - } else { - throw new Error('dom-bind requires a <template> child'); - } - }); - observer.observe(this, {childList: true}); - return; - } - this.root = this._stampTemplate( - /** @type {!HTMLTemplateElement} */(template)); - this.$ = this.root.$; - this.__children = []; - for (let n=this.root.firstChild; n; n=n.nextSibling) { - this.__children[this.__children.length] = n; - } - this._enableProperties(); - } - this.__insertChildren(); - this.dispatchEvent(new CustomEvent('dom-change', { - bubbles: true, - composed: true - })); - } - -} - -customElements.define('dom-bind', DomBind);
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-if.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-if.js deleted file mode 100644 index a13940a..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-if.js +++ /dev/null
@@ -1,289 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import { PolymerElement } from '../../polymer-element.js'; - -import { templatize } from '../utils/templatize.js'; -import { Debouncer } from '../utils/debounce.js'; -import { enqueueDebouncer, flush } from '../utils/flush.js'; -import { microTask } from '../utils/async.js'; -import { root } from '../utils/path.js'; -import { wrap } from '../utils/wrap.js'; - -/** - * The `<dom-if>` element will stamp a light-dom `<template>` child when - * the `if` property becomes truthy, and the template can use Polymer - * data-binding and declarative event features when used in the context of - * a Polymer element's template. - * - * When `if` becomes falsy, the stamped content is hidden but not - * removed from dom. When `if` subsequently becomes truthy again, the content - * is simply re-shown. This approach is used due to its favorable performance - * characteristics: the expense of creating template content is paid only - * once and lazily. - * - * Set the `restamp` property to true to force the stamped content to be - * created / destroyed when the `if` condition changes. - * - * @customElement - * @polymer - * @extends PolymerElement - * @summary Custom element that conditionally stamps and hides or removes - * template content based on a boolean flag. - */ -export class DomIf extends PolymerElement { - - // Not needed to find template; can be removed once the analyzer - // can find the tag name from customElements.define call - static get is() { return 'dom-if'; } - - static get template() { return null; } - - static get properties() { - - return { - - /** - * Fired whenever DOM is added or removed/hidden by this template (by - * default, rendering occurs lazily). To force immediate rendering, call - * `render`. - * - * @event dom-change - */ - - /** - * A boolean indicating whether this template should stamp. - */ - if: { - type: Boolean, - observer: '__debounceRender' - }, - - /** - * When true, elements will be removed from DOM and discarded when `if` - * becomes false and re-created and added back to the DOM when `if` - * becomes true. By default, stamped elements will be hidden but left - * in the DOM when `if` becomes false, which is generally results - * in better performance. - */ - restamp: { - type: Boolean, - observer: '__debounceRender' - } - - }; - - } - - constructor() { - super(); - this.__renderDebouncer = null; - this.__invalidProps = null; - this.__instance = null; - this._lastIf = false; - this.__ctor = null; - this.__hideTemplateChildren__ = false; - } - - __debounceRender() { - // Render is async for 2 reasons: - // 1. To eliminate dom creation trashing if user code thrashes `if` in the - // same turn. This was more common in 1.x where a compound computed - // property could result in the result changing multiple times, but is - // mitigated to a large extent by batched property processing in 2.x. - // 2. To avoid double object propagation when a bag including values bound - // to the `if` property as well as one or more hostProps could enqueue - // the <dom-if> to flush before the <template>'s host property - // forwarding. In that scenario creating an instance would result in - // the host props being set once, and then the enqueued changes on the - // template would set properties a second time, potentially causing an - // object to be set to an instance more than once. Creating the - // instance async from flushing data ensures this doesn't happen. If - // we wanted a sync option in the future, simply having <dom-if> flush - // (or clear) its template's pending host properties before creating - // the instance would also avoid the problem. - this.__renderDebouncer = Debouncer.debounce( - this.__renderDebouncer - , microTask - , () => this.__render()); - enqueueDebouncer(this.__renderDebouncer); - } - - /** - * @override - * @return {void} - */ - disconnectedCallback() { - super.disconnectedCallback(); - const parent = wrap(this).parentNode; - if (!parent || (parent.nodeType == Node.DOCUMENT_FRAGMENT_NODE && - !wrap(parent).host)) { - this.__teardownInstance(); - } - } - - /** - * @override - * @return {void} - */ - connectedCallback() { - super.connectedCallback(); - this.style.display = 'none'; - if (this.if) { - this.__debounceRender(); - } - } - - /** - * Forces the element to render its content. Normally rendering is - * asynchronous to a provoking change. This is done for efficiency so - * that multiple changes trigger only a single render. The render method - * should be called if, for example, template rendering is required to - * validate application state. - * @return {void} - */ - render() { - flush(); - } - - __render() { - if (this.if) { - if (!this.__ensureInstance()) { - // No template found yet - return; - } - this._showHideChildren(); - } else if (this.restamp) { - this.__teardownInstance(); - } - if (!this.restamp && this.__instance) { - this._showHideChildren(); - } - if (this.if != this._lastIf) { - this.dispatchEvent(new CustomEvent('dom-change', { - bubbles: true, - composed: true - })); - this._lastIf = this.if; - } - } - - __ensureInstance() { - let parentNode = wrap(this).parentNode; - // Guard against element being detached while render was queued - if (parentNode) { - if (!this.__ctor) { - let template = /** @type {HTMLTemplateElement} */(wrap(this).querySelector('template')); - if (!template) { - // Wait until childList changes and template should be there by then - let observer = new MutationObserver(() => { - if (wrap(this).querySelector('template')) { - observer.disconnect(); - this.__render(); - } else { - throw new Error('dom-if requires a <template> child'); - } - }); - observer.observe(this, {childList: true}); - return false; - } - this.__ctor = templatize(template, this, { - // dom-if templatizer instances require `mutable: true`, as - // `__syncHostProperties` relies on that behavior to sync objects - mutableData: true, - /** - * @param {string} prop Property to forward - * @param {*} value Value of property - * @this {DomIf} - */ - forwardHostProp: function(prop, value) { - if (this.__instance) { - if (this.if) { - this.__instance.forwardHostProp(prop, value); - } else { - // If we have an instance but are squelching host property - // forwarding due to if being false, note the invalidated - // properties so `__syncHostProperties` can sync them the next - // time `if` becomes true - this.__invalidProps = this.__invalidProps || Object.create(null); - this.__invalidProps[root(prop)] = true; - } - } - } - }); - } - if (!this.__instance) { - this.__instance = new this.__ctor(); - wrap(parentNode).insertBefore(this.__instance.root, this); - } else { - this.__syncHostProperties(); - let c$ = this.__instance.children; - if (c$ && c$.length) { - // Detect case where dom-if was re-attached in new position - let lastChild = wrap(this).previousSibling; - if (lastChild !== c$[c$.length-1]) { - for (let i=0, n; (i<c$.length) && (n=c$[i]); i++) { - wrap(parentNode).insertBefore(n, this); - } - } - } - } - } - return true; - } - - __syncHostProperties() { - let props = this.__invalidProps; - if (props) { - for (let prop in props) { - this.__instance._setPendingProperty(prop, this.__dataHost[prop]); - } - this.__invalidProps = null; - this.__instance._flushProperties(); - } - } - - __teardownInstance() { - if (this.__instance) { - let c$ = this.__instance.children; - if (c$ && c$.length) { - // use first child parent, for case when dom-if may have been detached - let parent = wrap(c$[0]).parentNode; - // Instance children may be disconnected from parents when dom-if - // detaches if a tree was innerHTML'ed - if (parent) { - parent = wrap(parent); - for (let i=0, n; (i<c$.length) && (n=c$[i]); i++) { - parent.removeChild(n); - } - } - } - this.__instance = null; - this.__invalidProps = null; - } - } - - /** - * Shows or hides the template instance top level child elements. For - * text nodes, `textContent` is removed while "hidden" and replaced when - * "shown." - * @return {void} - * @protected - * @suppress {visibility} - */ - _showHideChildren() { - let hidden = this.__hideTemplateChildren__ || !this.if; - if (this.__instance) { - this.__instance._showHideChildren(hidden); - } - } - -} - -customElements.define(DomIf.is, DomIf);
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-module.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-module.js deleted file mode 100644 index 02c41e7..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-module.js +++ /dev/null
@@ -1,163 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import '../utils/boot.js'; - -import { resolveUrl, pathFromUrl } from '../utils/resolve-url.js'; -import { strictTemplatePolicy } from '../utils/settings.js'; - -let modules = {}; -let lcModules = {}; -/** - * Sets a dom-module into the global registry by id. - * - * @param {string} id dom-module id - * @param {DomModule} module dom-module instance - * @return {void} - */ -function setModule(id, module) { - // store id separate from lowercased id so that - // in all cases mixedCase id will stored distinctly - // and lowercase version is a fallback - modules[id] = lcModules[id.toLowerCase()] = module; -} -/** - * Retrieves a dom-module from the global registry by id. - * - * @param {string} id dom-module id - * @return {DomModule!} dom-module instance - */ -function findModule(id) { - return modules[id] || lcModules[id.toLowerCase()]; -} - -function styleOutsideTemplateCheck(inst) { - if (inst.querySelector('style')) { - console.warn('dom-module %s has style outside template', inst.id); - } -} - -/** - * The `dom-module` element registers the dom it contains to the name given - * by the module's id attribute. It provides a unified database of dom - * accessible via its static `import` API. - * - * A key use case of `dom-module` is for providing custom element `<template>`s - * via HTML imports that are parsed by the native HTML parser, that can be - * relocated during a bundling pass and still looked up by `id`. - * - * Example: - * - * <dom-module id="foo"> - * <img src="stuff.png"> - * </dom-module> - * - * Then in code in some other location that cannot access the dom-module above - * - * let img = customElements.get('dom-module').import('foo', 'img'); - * - * @customElement - * @extends HTMLElement - * @summary Custom element that provides a registry of relocatable DOM content - * by `id` that is agnostic to bundling. - * @unrestricted - */ -export class DomModule extends HTMLElement { - - static get observedAttributes() { return ['id']; } - - /** - * Retrieves the element specified by the css `selector` in the module - * registered by `id`. For example, this.import('foo', 'img'); - * @param {string} id The id of the dom-module in which to search. - * @param {string=} selector The css selector by which to find the element. - * @return {Element} Returns the element which matches `selector` in the - * module registered at the specified `id`. - * - * @export - * @nocollapse Referred to indirectly in style-gather.js - */ - static import(id, selector) { - if (id) { - let m = findModule(id); - if (m && selector) { - return m.querySelector(selector); - } - return m; - } - return null; - } - - /* eslint-disable no-unused-vars */ - /** - * @param {string} name Name of attribute. - * @param {?string} old Old value of attribute. - * @param {?string} value Current value of attribute. - * @param {?string} namespace Attribute namespace. - * @return {void} - * @override - */ - attributeChangedCallback(name, old, value, namespace) { - if (old !== value) { - this.register(); - } - } - /* eslint-enable no-unused-args */ - - /** - * The absolute URL of the original location of this `dom-module`. - * - * This value will differ from this element's `ownerDocument` in the - * following ways: - * - Takes into account any `assetpath` attribute added during bundling - * to indicate the original location relative to the bundled location - * - Uses the HTMLImports polyfill's `importForElement` API to ensure - * the path is relative to the import document's location since - * `ownerDocument` is not currently polyfilled - */ - get assetpath() { - // Don't override existing assetpath. - if (!this.__assetpath) { - // note: assetpath set via an attribute must be relative to this - // element's location; accomodate polyfilled HTMLImports - const owner = window.HTMLImports && HTMLImports.importForElement ? - HTMLImports.importForElement(this) || document : this.ownerDocument; - const url = resolveUrl( - this.getAttribute('assetpath') || '', owner.baseURI); - this.__assetpath = pathFromUrl(url); - } - return this.__assetpath; - } - - /** - * Registers the dom-module at a given id. This method should only be called - * when a dom-module is imperatively created. For - * example, `document.createElement('dom-module').register('foo')`. - * @param {string=} id The id at which to register the dom-module. - * @return {void} - */ - register(id) { - id = id || this.id; - if (id) { - // Under strictTemplatePolicy, reject and null out any re-registered - // dom-module since it is ambiguous whether first-in or last-in is trusted - if (strictTemplatePolicy && findModule(id) !== undefined) { - setModule(id, null); - throw new Error(`strictTemplatePolicy: dom-module ${id} re-registered`); - } - this.id = id; - setModule(id, this); - styleOutsideTemplateCheck(this); - } - } -} - -DomModule.prototype['modules'] = modules; - -customElements.define('dom-module', DomModule);
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-repeat.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-repeat.js deleted file mode 100644 index 74f1265..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-repeat.js +++ /dev/null
@@ -1,733 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import { PolymerElement } from '../../polymer-element.js'; - -import { TemplateInstanceBase, templatize, modelForElement } from '../utils/templatize.js'; // eslint-disable-line no-unused-vars -import { Debouncer } from '../utils/debounce.js'; -import { enqueueDebouncer, flush } from '../utils/flush.js'; -import { OptionalMutableData } from '../mixins/mutable-data.js'; -import { matches, translate } from '../utils/path.js'; -import { timeOut, microTask } from '../utils/async.js'; -import { wrap } from '../utils/wrap.js'; - -/** - * @constructor - * @implements {Polymer_OptionalMutableData} - * @extends {PolymerElement} - * @private - */ -const domRepeatBase = OptionalMutableData(PolymerElement); - -/** - * The `<dom-repeat>` element will automatically stamp and binds one instance - * of template content to each object in a user-provided array. - * `dom-repeat` accepts an `items` property, and one instance of the template - * is stamped for each item into the DOM at the location of the `dom-repeat` - * element. The `item` property will be set on each instance's binding - * scope, thus templates should bind to sub-properties of `item`. - * - * Example: - * - * ```html - * <dom-module id="employee-list"> - * - * <template> - * - * <div> Employee list: </div> - * <dom-repeat items="{{employees}}"> - * <template> - * <div>First name: <span>{{item.first}}</span></div> - * <div>Last name: <span>{{item.last}}</span></div> - * </template> - * </dom-repeat> - * - * </template> - * - * </dom-module> - * ``` - * - * With the following custom element definition: - * - * ```js - * class EmployeeList extends PolymerElement { - * static get is() { return 'employee-list'; } - * static get properties() { - * return { - * employees: { - * value() { - * return [ - * {first: 'Bob', last: 'Smith'}, - * {first: 'Sally', last: 'Johnson'}, - * ... - * ]; - * } - * } - * }; - * } - * } - * ``` - * - * Notifications for changes to items sub-properties will be forwarded to template - * instances, which will update via the normal structured data notification system. - * - * Mutations to the `items` array itself should be made using the Array - * mutation API's on the PropertyEffects mixin (`push`, `pop`, `splice`, - * `shift`, `unshift`), and template instances will be kept in sync with the - * data in the array. - * - * Events caught by event handlers within the `dom-repeat` template will be - * decorated with a `model` property, which represents the binding scope for - * each template instance. The model should be used to manipulate data on the - * instance, for example `event.model.set('item.checked', true);`. - * - * Alternatively, the model for a template instance for an element stamped by - * a `dom-repeat` can be obtained using the `modelForElement` API on the - * `dom-repeat` that stamped it, for example - * `this.$.domRepeat.modelForElement(event.target).set('item.checked', true);`. - * This may be useful for manipulating instance data of event targets obtained - * by event handlers on parents of the `dom-repeat` (event delegation). - * - * A view-specific filter/sort may be applied to each `dom-repeat` by supplying a - * `filter` and/or `sort` property. This may be a string that names a function on - * the host, or a function may be assigned to the property directly. The functions - * should implemented following the standard `Array` filter/sort API. - * - * In order to re-run the filter or sort functions based on changes to sub-fields - * of `items`, the `observe` property may be set as a space-separated list of - * `item` sub-fields that should cause a re-filter/sort when modified. If - * the filter or sort function depends on properties not contained in `items`, - * the user should observe changes to those properties and call `render` to update - * the view based on the dependency change. - * - * For example, for an `dom-repeat` with a filter of the following: - * - * ```js - * isEngineer(item) { - * return item.type == 'engineer' || item.manager.type == 'engineer'; - * } - * ``` - * - * Then the `observe` property should be configured as follows: - * - * ```html - * <dom-repeat items="{{employees}}" filter="isEngineer" observe="type manager.type"> - * ``` - * - * @customElement - * @polymer - * @extends {domRepeatBase} - * @appliesMixin OptionalMutableData - * @summary Custom element for stamping instance of a template bound to - * items in an array. - */ -export class DomRepeat extends domRepeatBase { - - // Not needed to find template; can be removed once the analyzer - // can find the tag name from customElements.define call - static get is() { return 'dom-repeat'; } - - static get template() { return null; } - - static get properties() { - - /** - * Fired whenever DOM is added or removed by this template (by - * default, rendering occurs lazily). To force immediate rendering, call - * `render`. - * - * @event dom-change - */ - return { - - /** - * An array containing items determining how many instances of the template - * to stamp and that that each template instance should bind to. - */ - items: { - type: Array - }, - - /** - * The name of the variable to add to the binding scope for the array - * element associated with a given template instance. - */ - as: { - type: String, - value: 'item' - }, - - /** - * The name of the variable to add to the binding scope with the index - * of the instance in the sorted and filtered list of rendered items. - * Note, for the index in the `this.items` array, use the value of the - * `itemsIndexAs` property. - */ - indexAs: { - type: String, - value: 'index' - }, - - /** - * The name of the variable to add to the binding scope with the index - * of the instance in the `this.items` array. Note, for the index of - * this instance in the sorted and filtered list of rendered items, - * use the value of the `indexAs` property. - */ - itemsIndexAs: { - type: String, - value: 'itemsIndex' - }, - - /** - * A function that should determine the sort order of the items. This - * property should either be provided as a string, indicating a method - * name on the element's host, or else be an actual function. The - * function should match the sort function passed to `Array.sort`. - * Using a sort function has no effect on the underlying `items` array. - */ - sort: { - type: Function, - observer: '__sortChanged' - }, - - /** - * A function that can be used to filter items out of the view. This - * property should either be provided as a string, indicating a method - * name on the element's host, or else be an actual function. The - * function should match the sort function passed to `Array.filter`. - * Using a filter function has no effect on the underlying `items` array. - */ - filter: { - type: Function, - observer: '__filterChanged' - }, - - /** - * When using a `filter` or `sort` function, the `observe` property - * should be set to a space-separated list of the names of item - * sub-fields that should trigger a re-sort or re-filter when changed. - * These should generally be fields of `item` that the sort or filter - * function depends on. - */ - observe: { - type: String, - observer: '__observeChanged' - }, - - /** - * When using a `filter` or `sort` function, the `delay` property - * determines a debounce time in ms after a change to observed item - * properties that must pass before the filter or sort is re-run. - * This is useful in rate-limiting shuffling of the view when - * item changes may be frequent. - */ - delay: Number, - - /** - * Count of currently rendered items after `filter` (if any) has been applied. - * If "chunking mode" is enabled, `renderedItemCount` is updated each time a - * set of template instances is rendered. - * - */ - renderedItemCount: { - type: Number, - notify: true, - readOnly: true - }, - - /** - * Defines an initial count of template instances to render after setting - * the `items` array, before the next paint, and puts the `dom-repeat` - * into "chunking mode". The remaining items will be created and rendered - * incrementally at each animation frame therof until all instances have - * been rendered. - */ - initialCount: { - type: Number, - observer: '__initializeChunking' - }, - - /** - * When `initialCount` is used, this property defines a frame rate (in - * fps) to target by throttling the number of instances rendered each - * frame to not exceed the budget for the target frame rate. The - * framerate is effectively the number of `requestAnimationFrame`s that - * it tries to allow to actually fire in a given second. It does this - * by measuring the time between `rAF`s and continuously adjusting the - * number of items created each `rAF` to maintain the target framerate. - * Setting this to a higher number allows lower latency and higher - * throughput for event handlers and other tasks, but results in a - * longer time for the remaining items to complete rendering. - */ - targetFramerate: { - type: Number, - value: 20 - }, - - _targetFrameTime: { - type: Number, - computed: '__computeFrameTime(targetFramerate)' - } - - }; - - } - - static get observers() { - return [ '__itemsChanged(items.*)' ]; - } - - constructor() { - super(); - this.__instances = []; - this.__limit = Infinity; - this.__pool = []; - this.__renderDebouncer = null; - this.__itemsIdxToInstIdx = {}; - this.__chunkCount = null; - this.__lastChunkTime = null; - this.__sortFn = null; - this.__filterFn = null; - this.__observePaths = null; - /** @type {?function(new:Polymer.TemplateInstanceBase, *)} */ - this.__ctor = null; - this.__isDetached = true; - this.template = null; - } - - /** - * @override - * @return {void} - */ - disconnectedCallback() { - super.disconnectedCallback(); - this.__isDetached = true; - for (let i=0; i<this.__instances.length; i++) { - this.__detachInstance(i); - } - } - - /** - * @override - * @return {void} - */ - connectedCallback() { - super.connectedCallback(); - this.style.display = 'none'; - // only perform attachment if the element was previously detached. - if (this.__isDetached) { - this.__isDetached = false; - let wrappedParent = wrap(wrap(this).parentNode); - for (let i=0; i<this.__instances.length; i++) { - this.__attachInstance(i, wrappedParent); - } - } - } - - __ensureTemplatized() { - // Templatizing (generating the instance constructor) needs to wait - // until ready, since won't have its template content handed back to - // it until then - if (!this.__ctor) { - let template = this.template = /** @type {HTMLTemplateElement} */(this.querySelector('template')); - if (!template) { - // // Wait until childList changes and template should be there by then - let observer = new MutationObserver(() => { - if (this.querySelector('template')) { - observer.disconnect(); - this.__render(); - } else { - throw new Error('dom-repeat requires a <template> child'); - } - }); - observer.observe(this, {childList: true}); - return false; - } - // Template instance props that should be excluded from forwarding - let instanceProps = {}; - instanceProps[this.as] = true; - instanceProps[this.indexAs] = true; - instanceProps[this.itemsIndexAs] = true; - this.__ctor = templatize(template, this, { - mutableData: this.mutableData, - parentModel: true, - instanceProps: instanceProps, - /** - * @this {DomRepeat} - * @param {string} prop Property to set - * @param {*} value Value to set property to - */ - forwardHostProp: function(prop, value) { - let i$ = this.__instances; - for (let i=0, inst; (i<i$.length) && (inst=i$[i]); i++) { - inst.forwardHostProp(prop, value); - } - }, - /** - * @this {DomRepeat} - * @param {Object} inst Instance to notify - * @param {string} prop Property to notify - * @param {*} value Value to notify - */ - notifyInstanceProp: function(inst, prop, value) { - if (matches(this.as, prop)) { - let idx = inst[this.itemsIndexAs]; - if (prop == this.as) { - this.items[idx] = value; - } - let path = translate(this.as, `${JSCompiler_renameProperty('items', this)}.${idx}`, prop); - this.notifyPath(path, value); - } - } - }); - } - return true; - } - - __getMethodHost() { - // Technically this should be the owner of the outermost template. - // In shadow dom, this is always getRootNode().host, but we can - // approximate this via cooperation with our dataHost always setting - // `_methodHost` as long as there were bindings (or id's) on this - // instance causing it to get a dataHost. - return this.__dataHost._methodHost || this.__dataHost; - } - - __functionFromPropertyValue(functionOrMethodName) { - if (typeof functionOrMethodName === 'string') { - let methodName = functionOrMethodName; - let obj = this.__getMethodHost(); - return function() { return obj[methodName].apply(obj, arguments); }; - } - - return functionOrMethodName; - } - - __sortChanged(sort) { - this.__sortFn = this.__functionFromPropertyValue(sort); - if (this.items) { this.__debounceRender(this.__render); } - } - - __filterChanged(filter) { - this.__filterFn = this.__functionFromPropertyValue(filter); - if (this.items) { this.__debounceRender(this.__render); } - } - - __computeFrameTime(rate) { - return Math.ceil(1000/rate); - } - - __initializeChunking() { - if (this.initialCount) { - this.__limit = this.initialCount; - this.__chunkCount = this.initialCount; - this.__lastChunkTime = performance.now(); - } - } - - __tryRenderChunk() { - // Debounced so that multiple calls through `_render` between animation - // frames only queue one new rAF (e.g. array mutation & chunked render) - if (this.items && this.__limit < this.items.length) { - this.__debounceRender(this.__requestRenderChunk); - } - } - - __requestRenderChunk() { - requestAnimationFrame(()=>this.__renderChunk()); - } - - __renderChunk() { - // Simple auto chunkSize throttling algorithm based on feedback loop: - // measure actual time between frames and scale chunk count by ratio - // of target/actual frame time - let currChunkTime = performance.now(); - let ratio = this._targetFrameTime / (currChunkTime - this.__lastChunkTime); - this.__chunkCount = Math.round(this.__chunkCount * ratio) || 1; - this.__limit += this.__chunkCount; - this.__lastChunkTime = currChunkTime; - this.__debounceRender(this.__render); - } - - __observeChanged() { - this.__observePaths = this.observe && - this.observe.replace('.*', '.').split(' '); - } - - __itemsChanged(change) { - if (this.items && !Array.isArray(this.items)) { - console.warn('dom-repeat expected array for `items`, found', this.items); - } - // If path was to an item (e.g. 'items.3' or 'items.3.foo'), forward the - // path to that instance synchronously (returns false for non-item paths) - if (!this.__handleItemPath(change.path, change.value)) { - // Otherwise, the array was reset ('items') or spliced ('items.splices'), - // so queue a full refresh - this.__initializeChunking(); - this.__debounceRender(this.__render); - } - } - - __handleObservedPaths(path) { - // Handle cases where path changes should cause a re-sort/filter - if (this.__sortFn || this.__filterFn) { - if (!path) { - // Always re-render if the item itself changed - this.__debounceRender(this.__render, this.delay); - } else if (this.__observePaths) { - // Otherwise, re-render if the path changed matches an observed path - let paths = this.__observePaths; - for (let i=0; i<paths.length; i++) { - if (path.indexOf(paths[i]) === 0) { - this.__debounceRender(this.__render, this.delay); - } - } - } - } - } - - /** - * @param {function(this:DomRepeat)} fn Function to debounce. - * @param {number=} delay Delay in ms to debounce by. - */ - __debounceRender(fn, delay = 0) { - this.__renderDebouncer = Debouncer.debounce( - this.__renderDebouncer - , delay > 0 ? timeOut.after(delay) : microTask - , fn.bind(this)); - enqueueDebouncer(this.__renderDebouncer); - } - - /** - * Forces the element to render its content. Normally rendering is - * asynchronous to a provoking change. This is done for efficiency so - * that multiple changes trigger only a single render. The render method - * should be called if, for example, template rendering is required to - * validate application state. - * @return {void} - */ - render() { - // Queue this repeater, then flush all in order - this.__debounceRender(this.__render); - flush(); - } - - __render() { - if (!this.__ensureTemplatized()) { - // No template found yet - return; - } - this.__applyFullRefresh(); - // Reset the pool - // TODO(kschaaf): Reuse pool across turns and nested templates - // Now that objects/arrays are re-evaluated when set, we can safely - // reuse pooled instances across turns, however we still need to decide - // semantics regarding how long to hold, how many to hold, etc. - this.__pool.length = 0; - // Set rendered item count - this._setRenderedItemCount(this.__instances.length); - // Notify users - this.dispatchEvent(new CustomEvent('dom-change', { - bubbles: true, - composed: true - })); - // Check to see if we need to render more items - this.__tryRenderChunk(); - } - - __applyFullRefresh() { - let items = this.items || []; - let isntIdxToItemsIdx = new Array(items.length); - for (let i=0; i<items.length; i++) { - isntIdxToItemsIdx[i] = i; - } - // Apply user filter - if (this.__filterFn) { - isntIdxToItemsIdx = isntIdxToItemsIdx.filter((i, idx, array) => - this.__filterFn(items[i], idx, array)); - } - // Apply user sort - if (this.__sortFn) { - isntIdxToItemsIdx.sort((a, b) => this.__sortFn(items[a], items[b])); - } - // items->inst map kept for item path forwarding - const itemsIdxToInstIdx = this.__itemsIdxToInstIdx = {}; - let instIdx = 0; - // Generate instances and assign items - const limit = Math.min(isntIdxToItemsIdx.length, this.__limit); - for (; instIdx<limit; instIdx++) { - let inst = this.__instances[instIdx]; - let itemIdx = isntIdxToItemsIdx[instIdx]; - let item = items[itemIdx]; - itemsIdxToInstIdx[itemIdx] = instIdx; - if (inst) { - inst._setPendingProperty(this.as, item); - inst._setPendingProperty(this.indexAs, instIdx); - inst._setPendingProperty(this.itemsIndexAs, itemIdx); - inst._flushProperties(); - } else { - this.__insertInstance(item, instIdx, itemIdx); - } - } - // Remove any extra instances from previous state - for (let i=this.__instances.length-1; i>=instIdx; i--) { - this.__detachAndRemoveInstance(i); - } - } - - __detachInstance(idx) { - let inst = this.__instances[idx]; - const wrappedRoot = wrap(inst.root); - for (let i=0; i<inst.children.length; i++) { - let el = inst.children[i]; - wrappedRoot.appendChild(el); - } - return inst; - } - - __attachInstance(idx, parent) { - let inst = this.__instances[idx]; - // Note, this is pre-wrapped as an optimization - parent.insertBefore(inst.root, this); - } - - __detachAndRemoveInstance(idx) { - let inst = this.__detachInstance(idx); - if (inst) { - this.__pool.push(inst); - } - this.__instances.splice(idx, 1); - } - - __stampInstance(item, instIdx, itemIdx) { - let model = {}; - model[this.as] = item; - model[this.indexAs] = instIdx; - model[this.itemsIndexAs] = itemIdx; - return new this.__ctor(model); - } - - __insertInstance(item, instIdx, itemIdx) { - let inst = this.__pool.pop(); - if (inst) { - // TODO(kschaaf): If the pool is shared across turns, hostProps - // need to be re-set to reused instances in addition to item - inst._setPendingProperty(this.as, item); - inst._setPendingProperty(this.indexAs, instIdx); - inst._setPendingProperty(this.itemsIndexAs, itemIdx); - inst._flushProperties(); - } else { - inst = this.__stampInstance(item, instIdx, itemIdx); - } - let beforeRow = this.__instances[instIdx + 1]; - let beforeNode = beforeRow ? beforeRow.children[0] : this; - wrap(wrap(this).parentNode).insertBefore(inst.root, beforeNode); - this.__instances[instIdx] = inst; - return inst; - } - - // Implements extension point from Templatize mixin - /** - * Shows or hides the template instance top level child elements. For - * text nodes, `textContent` is removed while "hidden" and replaced when - * "shown." - * @param {boolean} hidden Set to true to hide the children; - * set to false to show them. - * @return {void} - * @protected - */ - _showHideChildren(hidden) { - for (let i=0; i<this.__instances.length; i++) { - this.__instances[i]._showHideChildren(hidden); - } - } - - // Called as a side effect of a host items.<key>.<path> path change, - // responsible for notifying item.<path> changes to inst for key - __handleItemPath(path, value) { - let itemsPath = path.slice(6); // 'items.'.length == 6 - let dot = itemsPath.indexOf('.'); - let itemsIdx = dot < 0 ? itemsPath : itemsPath.substring(0, dot); - // If path was index into array... - if (itemsIdx == parseInt(itemsIdx, 10)) { - let itemSubPath = dot < 0 ? '' : itemsPath.substring(dot+1); - // If the path is observed, it will trigger a full refresh - this.__handleObservedPaths(itemSubPath); - // Note, even if a rull refresh is triggered, always do the path - // notification because unless mutableData is used for dom-repeat - // and all elements in the instance subtree, a full refresh may - // not trigger the proper update. - let instIdx = this.__itemsIdxToInstIdx[itemsIdx]; - let inst = this.__instances[instIdx]; - if (inst) { - let itemPath = this.as + (itemSubPath ? '.' + itemSubPath : ''); - // This is effectively `notifyPath`, but avoids some of the overhead - // of the public API - inst._setPendingPropertyOrPath(itemPath, value, false, true); - inst._flushProperties(); - } - return true; - } - } - - /** - * Returns the item associated with a given element stamped by - * this `dom-repeat`. - * - * Note, to modify sub-properties of the item, - * `modelForElement(el).set('item.<sub-prop>', value)` - * should be used. - * - * @param {!HTMLElement} el Element for which to return the item. - * @return {*} Item associated with the element. - */ - itemForElement(el) { - let instance = this.modelForElement(el); - return instance && instance[this.as]; - } - - /** - * Returns the inst index for a given element stamped by this `dom-repeat`. - * If `sort` is provided, the index will reflect the sorted order (rather - * than the original array order). - * - * @param {!HTMLElement} el Element for which to return the index. - * @return {?number} Row index associated with the element (note this may - * not correspond to the array index if a user `sort` is applied). - */ - indexForElement(el) { - let instance = this.modelForElement(el); - return instance && instance[this.indexAs]; - } - - /** - * Returns the template "model" associated with a given element, which - * serves as the binding scope for the template instance the element is - * contained in. A template model - * should be used to manipulate data associated with this template instance. - * - * Example: - * - * let model = modelForElement(el); - * if (model.index < 10) { - * model.set('item.checked', true); - * } - * - * @param {!HTMLElement} el Element for which to return a template model. - * @return {TemplateInstanceBase} Model representing the binding scope for - * the element. - */ - modelForElement(el) { - return modelForElement(this.template, el); - } - -} - -customElements.define(DomRepeat.is, DomRepeat);
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/class.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/class.js deleted file mode 100644 index a35c0c5c..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/class.js +++ /dev/null
@@ -1,534 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -import { LegacyElementMixin } from './legacy-element-mixin.js'; -import { legacyOptimizations } from '../utils/settings.js'; - -const lifecycleProps = { - attached: true, - detached: true, - ready: true, - created: true, - beforeRegister: true, - registered: true, - attributeChanged: true, - listeners: true, - hostAttributes: true -}; - -const excludeOnInfo = { - attached: true, - detached: true, - ready: true, - created: true, - beforeRegister: true, - registered: true, - attributeChanged: true, - behaviors: true, - _noAccessors: true -}; - -const excludeOnBehaviors = Object.assign({ - listeners: true, - hostAttributes: true, - properties: true, - observers: true, -}, excludeOnInfo); - -function copyProperties(source, target, excludeProps) { - const noAccessors = source._noAccessors; - const propertyNames = Object.getOwnPropertyNames(source); - for (let i = 0; i < propertyNames.length; i++) { - let p = propertyNames[i]; - if (p in excludeProps) { - continue; - } - if (noAccessors) { - target[p] = source[p]; - } else { - let pd = Object.getOwnPropertyDescriptor(source, p); - if (pd) { - // ensure property is configurable so that a later behavior can - // re-configure it. - pd.configurable = true; - Object.defineProperty(target, p, pd); - } - } - } -} - -/** - * Applies a "legacy" behavior or array of behaviors to the provided class. - * - * Note: this method will automatically also apply the `LegacyElementMixin` - * to ensure that any legacy behaviors can rely on legacy Polymer API on - * the underlying element. - * - * @function - * @template T - * @param {!Object|!Array<!Object>} behaviors Behavior object or array of behaviors. - * @param {function(new:T)} klass Element class. - * @return {?} Returns a new Element class extended by the - * passed in `behaviors` and also by `LegacyElementMixin`. - * @suppress {invalidCasts, checkTypes} - */ -export function mixinBehaviors(behaviors, klass) { - return GenerateClassFromInfo({}, LegacyElementMixin(klass), behaviors); -} - -// NOTE: -// 1.x -// Behaviors were mixed in *in reverse order* and de-duped on the fly. -// The rule was that behavior properties were copied onto the element -// prototype if and only if the property did not already exist. -// Given: Polymer{ behaviors: [A, B, C, A, B]}, property copy order was: -// (1), B, (2), A, (3) C. This means prototype properties win over -// B properties win over A win over C. This mirrors what would happen -// with inheritance if element extended B extended A extended C. -// -// Again given, Polymer{ behaviors: [A, B, C, A, B]}, the resulting -// `behaviors` array was [C, A, B]. -// Behavior lifecycle methods were called in behavior array order -// followed by the element, e.g. (1) C.created, (2) A.created, -// (3) B.created, (4) element.created. There was no support for -// super, and "super-behavior" methods were callable only by name). -// -// 2.x -// Behaviors are made into proper mixins which live in the -// element's prototype chain. Behaviors are placed in the element prototype -// eldest to youngest and de-duped youngest to oldest: -// So, first [A, B, C, A, B] becomes [C, A, B] then, -// the element prototype becomes (oldest) (1) PolymerElement, (2) class(C), -// (3) class(A), (4) class(B), (5) class(Polymer({...})). -// Result: -// This means element properties win over B properties win over A win -// over C. (same as 1.x) -// If lifecycle is called (super then me), order is -// (1) C.created, (2) A.created, (3) B.created, (4) element.created -// (again same as 1.x) -function applyBehaviors(proto, behaviors, lifecycle) { - for (let i=0; i<behaviors.length; i++) { - applyInfo(proto, behaviors[i], lifecycle, excludeOnBehaviors); - } -} - -function applyInfo(proto, info, lifecycle, excludeProps) { - copyProperties(info, proto, excludeProps); - for (let p in lifecycleProps) { - if (info[p]) { - lifecycle[p] = lifecycle[p] || []; - lifecycle[p].push(info[p]); - } - } -} - -/** - * @param {Array} behaviors List of behaviors to flatten. - * @param {Array=} list Target list to flatten behaviors into. - * @param {Array=} exclude List of behaviors to exclude from the list. - * @return {!Array} Returns the list of flattened behaviors. - */ -function flattenBehaviors(behaviors, list, exclude) { - list = list || []; - for (let i=behaviors.length-1; i >= 0; i--) { - let b = behaviors[i]; - if (b) { - if (Array.isArray(b)) { - flattenBehaviors(b, list); - } else { - // dedup - if (list.indexOf(b) < 0 && (!exclude || exclude.indexOf(b) < 0)) { - list.unshift(b); - } - } - } else { - console.warn('behavior is null, check for missing or 404 import'); - } - } - return list; -} - -/** - * Copies property descriptors from source to target, overwriting all fields - * of any previous descriptor for a property *except* for `value`, which is - * merged in from the target if it does not exist on the source. - * - * @param {*} target Target properties object - * @param {*} source Source properties object - */ -function mergeProperties(target, source) { - for (const p in source) { - const targetInfo = target[p]; - const sourceInfo = source[p]; - if (!('value' in sourceInfo) && targetInfo && ('value' in targetInfo)) { - target[p] = Object.assign({value: targetInfo.value}, sourceInfo); - } else { - target[p] = sourceInfo; - } - } -} - -/* Note about construction and extension of legacy classes. - [Changed in Q4 2018 to optimize performance.] - - When calling `Polymer` or `mixinBehaviors`, the generated class below is - made. The list of behaviors was previously made into one generated class per - behavior, but this is no longer the case as behaviors are now called - manually. Note, there may *still* be multiple generated classes in the - element's prototype chain if extension is used with `mixinBehaviors`. - - The generated class is directly tied to the info object and behaviors - used to create it. That list of behaviors is filtered so it's only the - behaviors not active on the superclass. In order to call through to the - entire list of lifecycle methods, it's important to call `super`. - - The element's `properties` and `observers` are controlled via the finalization - mechanism provided by `PropertiesMixin`. `Properties` and `observers` are - collected by manually traversing the prototype chain and merging. - - To limit changes, the `_registered` method is called via `_initializeProperties` - and not `_finalizeClass`. - -*/ -/** - * @param {!PolymerInit} info Polymer info object - * @param {function(new:HTMLElement)} Base base class to extend with info object - * @param {Object=} behaviors behaviors to copy into the element - * @return {function(new:HTMLElement)} Generated class - * @suppress {checkTypes} - * @private - */ -function GenerateClassFromInfo(info, Base, behaviors) { - - // manages behavior and lifecycle processing (filled in after class definition) - let behaviorList; - const lifecycle = {}; - - /** @private */ - class PolymerGenerated extends Base { - - // explicitly not calling super._finalizeClass - static _finalizeClass() { - // if calling via a subclass that hasn't been generated, pass through to super - if (!this.hasOwnProperty(JSCompiler_renameProperty('generatedFrom', this))) { - super._finalizeClass(); - } else { - // interleave properties and observers per behavior and `info` - if (behaviorList) { - for (let i=0, b; i < behaviorList.length; i++) { - b = behaviorList[i]; - if (b.properties) { - this.createProperties(b.properties); - } - if (b.observers) { - this.createObservers(b.observers, b.properties); - } - } - } - if (info.properties) { - this.createProperties(info.properties); - } - if (info.observers) { - this.createObservers(info.observers, info.properties); - } - // make sure to prepare the element template - this._prepareTemplate(); - } - } - - static get properties() { - const properties = {}; - if (behaviorList) { - for (let i=0; i < behaviorList.length; i++) { - mergeProperties(properties, behaviorList[i].properties); - } - } - mergeProperties(properties, info.properties); - return properties; - } - - static get observers() { - let observers = []; - if (behaviorList) { - for (let i=0, b; i < behaviorList.length; i++) { - b = behaviorList[i]; - if (b.observers) { - observers = observers.concat(b.observers); - } - } - } - if (info.observers) { - observers = observers.concat(info.observers); - } - return observers; - } - - /** - * @return {void} - */ - created() { - super.created(); - const list = lifecycle.created; - if (list) { - for (let i=0; i < list.length; i++) { - list[i].call(this); - } - } - } - - /** - * @return {void} - */ - _registered() { - /* NOTE: `beforeRegister` is called here for bc, but the behavior - is different than in 1.x. In 1.0, the method was called *after* - mixing prototypes together but *before* processing of meta-objects. - However, dynamic effects can still be set here and can be done either - in `beforeRegister` or `registered`. It is no longer possible to set - `is` in `beforeRegister` as you could in 1.x. - */ - // only proceed if the generated class' prototype has not been registered. - const generatedProto = PolymerGenerated.prototype; - if (!generatedProto.hasOwnProperty('__hasRegisterFinished')) { - generatedProto.__hasRegisterFinished = true; - // ensure superclass is registered first. - super._registered(); - // copy properties onto the generated class lazily if we're optimizing, - if (legacyOptimizations) { - copyPropertiesToProto(generatedProto); - } - // make sure legacy lifecycle is called on the *element*'s prototype - // and not the generated class prototype; if the element has been - // extended, these are *not* the same. - const proto = Object.getPrototypeOf(this); - let list = lifecycle.beforeRegister; - if (list) { - for (let i=0; i < list.length; i++) { - list[i].call(proto); - } - } - list = lifecycle.registered; - if (list) { - for (let i=0; i < list.length; i++) { - list[i].call(proto); - } - } - } - } - - /** - * @return {void} - */ - _applyListeners() { - super._applyListeners(); - const list = lifecycle.listeners; - if (list) { - for (let i=0; i < list.length; i++) { - const listeners = list[i]; - if (listeners) { - for (let l in listeners) { - this._addMethodEventListenerToNode(this, l, listeners[l]); - } - } - } - } - } - - // note: exception to "super then me" rule; - // do work before calling super so that super attributes - // only apply if not already set. - /** - * @return {void} - */ - _ensureAttributes() { - const list = lifecycle.hostAttributes; - if (list) { - for (let i=list.length-1; i >= 0; i--) { - const hostAttributes = list[i]; - for (let a in hostAttributes) { - this._ensureAttribute(a, hostAttributes[a]); - } - } - } - super._ensureAttributes(); - } - - /** - * @return {void} - */ - ready() { - super.ready(); - let list = lifecycle.ready; - if (list) { - for (let i=0; i < list.length; i++) { - list[i].call(this); - } - } - } - - /** - * @return {void} - */ - attached() { - super.attached(); - let list = lifecycle.attached; - if (list) { - for (let i=0; i < list.length; i++) { - list[i].call(this); - } - } - } - - /** - * @return {void} - */ - detached() { - super.detached(); - let list = lifecycle.detached; - if (list) { - for (let i=0; i < list.length; i++) { - list[i].call(this); - } - } - } - - /** - * Implements native Custom Elements `attributeChangedCallback` to - * set an attribute value to a property via `_attributeToProperty`. - * - * @param {string} name Name of attribute that changed - * @param {?string} old Old attribute value - * @param {?string} value New attribute value - * @return {void} - */ - attributeChanged(name, old, value) { - super.attributeChanged(); - let list = lifecycle.attributeChanged; - if (list) { - for (let i=0; i < list.length; i++) { - list[i].call(this, name, old, value); - } - } - } - } - - // apply behaviors, note actual copying is done lazily at first instance creation - if (behaviors) { - // NOTE: ensure the behavior is extending a class with - // legacy element api. This is necessary since behaviors expect to be able - // to access 1.x legacy api. - if (!Array.isArray(behaviors)) { - behaviors = [behaviors]; - } - let superBehaviors = Base.prototype.behaviors; - // get flattened, deduped list of behaviors *not* already on super class - behaviorList = flattenBehaviors(behaviors, null, superBehaviors); - PolymerGenerated.prototype.behaviors = superBehaviors ? - superBehaviors.concat(behaviors) : behaviorList; - } - - const copyPropertiesToProto = (proto) => { - if (behaviorList) { - applyBehaviors(proto, behaviorList, lifecycle); - } - applyInfo(proto, info, lifecycle, excludeOnInfo); - }; - - // copy properties if we're not optimizing - if (!legacyOptimizations) { - copyPropertiesToProto(PolymerGenerated.prototype); - } - - PolymerGenerated.generatedFrom = info; - - return PolymerGenerated; -} - -/** - * Generates a class that extends `LegacyElement` based on the - * provided info object. Metadata objects on the `info` object - * (`properties`, `observers`, `listeners`, `behaviors`, `is`) are used - * for Polymer's meta-programming systems, and any functions are copied - * to the generated class. - * - * Valid "metadata" values are as follows: - * - * `is`: String providing the tag name to register the element under. In - * addition, if a `dom-module` with the same id exists, the first template - * in that `dom-module` will be stamped into the shadow root of this element, - * with support for declarative event listeners (`on-...`), Polymer data - * bindings (`[[...]]` and `{{...}}`), and id-based node finding into - * `this.$`. - * - * `properties`: Object describing property-related metadata used by Polymer - * features (key: property names, value: object containing property metadata). - * Valid keys in per-property metadata include: - * - `type` (String|Number|Object|Array|...): Used by - * `attributeChangedCallback` to determine how string-based attributes - * are deserialized to JavaScript property values. - * - `notify` (boolean): Causes a change in the property to fire a - * non-bubbling event called `<property>-changed`. Elements that have - * enabled two-way binding to the property use this event to observe changes. - * - `readOnly` (boolean): Creates a getter for the property, but no setter. - * To set a read-only property, use the private setter method - * `_setProperty(property, value)`. - * - `observer` (string): Observer method name that will be called when - * the property changes. The arguments of the method are - * `(value, previousValue)`. - * - `computed` (string): String describing method and dependent properties - * for computing the value of this property (e.g. `'computeFoo(bar, zot)'`). - * Computed properties are read-only by default and can only be changed - * via the return value of the computing method. - * - * `observers`: Array of strings describing multi-property observer methods - * and their dependent properties (e.g. `'observeABC(a, b, c)'`). - * - * `listeners`: Object describing event listeners to be added to each - * instance of this element (key: event name, value: method name). - * - * `behaviors`: Array of additional `info` objects containing metadata - * and callbacks in the same format as the `info` object here which are - * merged into this element. - * - * `hostAttributes`: Object listing attributes to be applied to the host - * once created (key: attribute name, value: attribute value). Values - * are serialized based on the type of the value. Host attributes should - * generally be limited to attributes such as `tabIndex` and `aria-...`. - * Attributes in `hostAttributes` are only applied if a user-supplied - * attribute is not already present (attributes in markup override - * `hostAttributes`). - * - * In addition, the following Polymer-specific callbacks may be provided: - * - `registered`: called after first instance of this element, - * - `created`: called during `constructor` - * - `attached`: called during `connectedCallback` - * - `detached`: called during `disconnectedCallback` - * - `ready`: called before first `attached`, after all properties of - * this element have been propagated to its template and all observers - * have run - * - * @param {!PolymerInit} info Object containing Polymer metadata and functions - * to become class methods. - * @template T - * @param {function(T):T} mixin Optional mixin to apply to legacy base class - * before extending with Polymer metaprogramming. - * @return {function(new:HTMLElement)} Generated class - */ -export const Class = function(info, mixin) { - if (!info) { - console.warn('Polymer.Class requires `info` argument'); - } - let klass = mixin ? mixin(LegacyElementMixin(HTMLElement)) : - LegacyElementMixin(HTMLElement); - klass = GenerateClassFromInfo(info, klass, info.behaviors); - // decorate klass with registration info - klass.is = klass.prototype.is = info.is; - return klass; -};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-data-mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-data-mixin.js deleted file mode 100644 index b2a2553..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-data-mixin.js +++ /dev/null
@@ -1,181 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -import { Class } from './class.js'; -import { Polymer } from '../../polymer-legacy.js'; -import { dedupingMixin } from '../utils/mixin.js'; -import { templatize } from '../utils/templatize.js'; - -const UndefinedArgumentError = class extends Error { - constructor(message, arg) { - super(message); - this.arg = arg; - this.name = this.constructor.name; - // Affordances for ensuring instanceof works after babel ES5 compilation - // TODO(kschaaf): Remove after polymer CLI updates to newer Babel that - // sets the constructor/prototype correctly for subclassed builtins - this.constructor = UndefinedArgumentError; - this.__proto__ = UndefinedArgumentError.prototype; - } -}; - -/** - * Wraps effect functions to catch `UndefinedArgumentError`s and warn. - * - * @param {Object=} effect Effect metadata object - * @param {Object=} fnName Name of user function, if known - * @return {?Object|undefined} Effect metadata object - */ -function wrapEffect(effect, fnName) { - if (effect && effect.fn) { - const fn = effect.fn; - effect.fn = function() { - try { - fn.apply(this, arguments); - } catch (e) { - if (e instanceof UndefinedArgumentError) { - console.warn(`Argument '${e.arg}'${fnName ?` for method '${fnName}'` : ''} was undefined. Ensure it has a default value, or else ensure the method handles the argument being undefined.`); - } else { - throw e; - } - } - }; - } - return effect; -} - -/** - * Mixin to selectively add back Polymer 1.x's `undefined` rules - * governing when observers & computing functions run based - * on all arguments being defined (reference https://www.polymer-project.org/1.0/docs/devguide/observers#multi-property-observers). - * - * When loaded, all legacy elements (defined with `Polymer({...})`) - * will have the mixin applied. The mixin only restores legacy data handling - * if `_legacyUndefinedCheck: true` is set on the element's prototype. - * - * This mixin is intended for use to help migration from Polymer 1.x to - * 2.x+ by allowing legacy code to work while identifying observers and - * computing functions that need undefined checks to work without - * the mixin in Polymer 2. - * - * @mixinFunction - * @polymer - * @summary Mixin to selectively add back Polymer 1.x's `undefined` rules - * governing when observers & computing functions run. - */ -export const LegacyDataMixin = dedupingMixin(superClass => { - - /** - * @unrestricted - * @private - */ - class LegacyDataMixin extends superClass { - /** - * Overrides `Polymer.PropertyEffects` to add `undefined` argument - * checking to match Polymer 1.x style rules - * - * @param {!Array<!MethodArg>} args Array of argument metadata - * @param {string} path Property/path name that triggered the method effect - * @param {Object} props Bag of current property changes - * @return {Array<*>} Array of argument values - * @private - */ - _marshalArgs(args, path, props) { - const vals = super._marshalArgs(args, path, props); - // Per legacy data rules, single-property observers (whether in `properties` - // and in `observers`) are called regardless of whether their argument is - // undefined or not. Multi-property observers must have all arguments defined - if (this._legacyUndefinedCheck && vals.length > 1) { - for (let i=0; i<vals.length; i++) { - if (vals[i] === undefined) { - // Break out of effect's control flow; will be caught in - // wrapped property effect function below - const name = args[i].name; - throw new UndefinedArgumentError(`Argument '${name}' is undefined.`, name); - } - } - } - return vals; - } - - /** - * Overrides `Polyer.PropertyEffects` to wrap effect functions to - * catch `UndefinedArgumentError`s and warn. - * - * @param {string} property Property that should trigger the effect - * @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES - * @param {Object=} effect Effect metadata object - * @return {void} - * @protected - */ - _addPropertyEffect(property, type, effect) { - return super._addPropertyEffect(property, type, - wrapEffect(effect, effect && effect.info && effect.info.methodName)); - } - - /** - * Overrides `Polyer.PropertyEffects` to wrap effect functions to - * catch `UndefinedArgumentError`s and warn. - * - * @param {Object} templateInfo Template metadata to add effect to - * @param {string} prop Property that should trigger the effect - * @param {Object=} effect Effect metadata object - * @return {void} - * @protected - */ - static _addTemplatePropertyEffect(templateInfo, prop, effect) { - return super._addTemplatePropertyEffect(templateInfo, prop, wrapEffect(effect)); - } - - } - - return LegacyDataMixin; - -}); - -// LegacyDataMixin is applied to base class _before_ metaprogramming, to -// ensure override of _addPropertyEffect et.al. are used by metaprogramming -// performed in _finalizeClass -Polymer.Class = (info, mixin) => Class(info, - superClass => mixin ? - mixin(LegacyDataMixin(superClass)) : - LegacyDataMixin(superClass) -); - -// Apply LegacyDataMixin to Templatizer instances as well, and defer -// runtime switch to the root's host (_methodHost) -/** - * @mixinFunction - * @polymer - */ -const TemplatizeMixin = - dedupingMixin(superClass => { - /** - * @constructor - * @extends {HTMLElement} - */ - const legacyBase = LegacyDataMixin(superClass); - /** - * @private - */ - class TemplateLegacy extends legacyBase { - get _legacyUndefinedCheck() { - return this._methodHost && this._methodHost._legacyUndefinedCheck; - } - } - /** @type {!Polymer_PropertyEffects} */ - TemplateLegacy.prototype._methodHost; - return TemplateLegacy; - }); - -templatize.mixin = TemplatizeMixin; - -console.info('LegacyDataMixin will be applied to all legacy elements.\n' + - 'Set `_legacyUndefinedCheck: true` on element class to enable.');
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-element-mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-element-mixin.js deleted file mode 100644 index bd591b6d..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-element-mixin.js +++ /dev/null
@@ -1,1059 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import '../../../shadycss/entrypoints/apply-shim.js'; -import { ElementMixin } from '../mixins/element-mixin.js'; -import { GestureEventListeners } from '../mixins/gesture-event-listeners.js'; -import { dedupingMixin } from '../utils/mixin.js'; -import '../utils/render-status.js'; -import '../utils/unresolved.js'; -import { dom, matchesSelector } from './polymer.dom.js'; -import { setTouchAction } from '../utils/gestures.js'; -import { Debouncer } from '../utils/debounce.js'; -import { timeOut, microTask } from '../utils/async.js'; -import { get } from '../utils/path.js'; -import { wrap } from '../utils/wrap.js'; - -let styleInterface = window.ShadyCSS; - -/** - * Element class mixin that provides Polymer's "legacy" API intended to be - * backward-compatible to the greatest extent possible with the API - * found on the Polymer 1.x `Polymer.Base` prototype applied to all elements - * defined using the `Polymer({...})` function. - * - * @mixinFunction - * @polymer - * @appliesMixin ElementMixin - * @appliesMixin GestureEventListeners - * @property isAttached {boolean} Set to `true` in this element's - * `connectedCallback` and `false` in `disconnectedCallback` - * @summary Element class mixin that provides Polymer's "legacy" API - */ -export const LegacyElementMixin = dedupingMixin((base) => { - /** - * @constructor - * @implements {Polymer_ElementMixin} - * @implements {Polymer_GestureEventListeners} - * @extends {HTMLElement} - * @private - */ - const legacyElementBase = GestureEventListeners(ElementMixin(base)); - - /** - * Map of simple names to touch action names - * @dict - */ - const DIRECTION_MAP = { - 'x': 'pan-x', - 'y': 'pan-y', - 'none': 'none', - 'all': 'auto' - }; - - /** - * @polymer - * @mixinClass - * @extends {legacyElementBase} - * @implements {Polymer_LegacyElementMixin} - * @unrestricted - */ - class LegacyElement extends legacyElementBase { - - constructor() { - super(); - /** @type {boolean} */ - this.isAttached; - /** @type {?WeakMap<!Element, !Object<string, !Function>>} */ - this.__boundListeners; - /** @type {?Object<string, ?Function>} */ - this._debouncers; - } - - /** - * Forwards `importMeta` from the prototype (i.e. from the info object - * passed to `Polymer({...})`) to the static API. - * - * @return {!Object} The `import.meta` object set on the prototype - * @suppress {missingProperties} `this` is always in the instance in - * closure for some reason even in a static method, rather than the class - */ - static get importMeta() { - return this.prototype.importMeta; - } - - /** - * Legacy callback called during the `constructor`, for overriding - * by the user. - * @override - * @return {void} - */ - created() {} - - /** - * Provides an implementation of `connectedCallback` - * which adds Polymer legacy API's `attached` method. - * @return {void} - * @override - */ - connectedCallback() { - super.connectedCallback(); - this.isAttached = true; - this.attached(); - } - - /** - * Legacy callback called during `connectedCallback`, for overriding - * by the user. - * @override - * @return {void} - */ - attached() {} - - /** - * Provides an implementation of `disconnectedCallback` - * which adds Polymer legacy API's `detached` method. - * @return {void} - * @override - */ - disconnectedCallback() { - super.disconnectedCallback(); - this.isAttached = false; - this.detached(); - } - - /** - * Legacy callback called during `disconnectedCallback`, for overriding - * by the user. - * @override - * @return {void} - */ - detached() {} - - /** - * Provides an override implementation of `attributeChangedCallback` - * which adds the Polymer legacy API's `attributeChanged` method. - * @param {string} name Name of attribute. - * @param {?string} old Old value of attribute. - * @param {?string} value Current value of attribute. - * @param {?string} namespace Attribute namespace. - * @return {void} - * @override - */ - attributeChangedCallback(name, old, value, namespace) { - if (old !== value) { - super.attributeChangedCallback(name, old, value, namespace); - this.attributeChanged(name, old, value); - } - } - - /** - * Legacy callback called during `attributeChangedChallback`, for overriding - * by the user. - * @param {string} name Name of attribute. - * @param {?string} old Old value of attribute. - * @param {?string} value Current value of attribute. - * @return {void} - * @override - */ - attributeChanged(name, old, value) {} // eslint-disable-line no-unused-vars - - /** - * Overrides the default `Polymer.PropertyEffects` implementation to - * add support for class initialization via the `_registered` callback. - * This is called only when the first instance of the element is created. - * - * @return {void} - * @override - * @suppress {invalidCasts} - */ - _initializeProperties() { - let proto = Object.getPrototypeOf(this); - if (!proto.hasOwnProperty('__hasRegisterFinished')) { - this._registered(); - // backstop in case the `_registered` implementation does not set this - proto.__hasRegisterFinished = true; - } - super._initializeProperties(); - this.root = /** @type {HTMLElement} */(this); - this.created(); - // Ensure listeners are applied immediately so that they are - // added before declarative event listeners. This allows an element to - // decorate itself via an event prior to any declarative listeners - // seeing the event. Note, this ensures compatibility with 1.x ordering. - this._applyListeners(); - } - - /** - * Called automatically when an element is initializing. - * Users may override this method to perform class registration time - * work. The implementation should ensure the work is performed - * only once for the class. - * @protected - * @return {void} - * @override - */ - _registered() {} - - /** - * Overrides the default `Polymer.PropertyEffects` implementation to - * add support for installing `hostAttributes` and `listeners`. - * - * @return {void} - * @override - */ - ready() { - this._ensureAttributes(); - super.ready(); - } - - /** - * Ensures an element has required attributes. Called when the element - * is being readied via `ready`. Users should override to set the - * element's required attributes. The implementation should be sure - * to check and not override existing attributes added by - * the user of the element. Typically, setting attributes should be left - * to the element user and not done here; reasonable exceptions include - * setting aria roles and focusability. - * @protected - * @return {void} - * @override - */ - _ensureAttributes() {} - - /** - * Adds element event listeners. Called when the element - * is being readied via `ready`. Users should override to - * add any required element event listeners. - * In performance critical elements, the work done here should be kept - * to a minimum since it is done before the element is rendered. In - * these elements, consider adding listeners asynchronously so as not to - * block render. - * @protected - * @return {void} - * @override - */ - _applyListeners() {} - - /** - * Converts a typed JavaScript value to a string. - * - * Note this method is provided as backward-compatible legacy API - * only. It is not directly called by any Polymer features. To customize - * how properties are serialized to attributes for attribute bindings and - * `reflectToAttribute: true` properties as well as this method, override - * the `_serializeValue` method provided by `Polymer.PropertyAccessors`. - * - * @param {*} value Value to deserialize - * @return {string | undefined} Serialized value - * @override - */ - serialize(value) { - return this._serializeValue(value); - } - - /** - * Converts a string to a typed JavaScript value. - * - * Note this method is provided as backward-compatible legacy API - * only. It is not directly called by any Polymer features. To customize - * how attributes are deserialized to properties for in - * `attributeChangedCallback`, override `_deserializeValue` method - * provided by `Polymer.PropertyAccessors`. - * - * @param {string} value String to deserialize - * @param {*} type Type to deserialize the string to - * @return {*} Returns the deserialized value in the `type` given. - * @override - */ - deserialize(value, type) { - return this._deserializeValue(value, type); - } - - /** - * Serializes a property to its associated attribute. - * - * Note this method is provided as backward-compatible legacy API - * only. It is not directly called by any Polymer features. - * - * @param {string} property Property name to reflect. - * @param {string=} attribute Attribute name to reflect. - * @param {*=} value Property value to reflect. - * @return {void} - * @override - */ - reflectPropertyToAttribute(property, attribute, value) { - this._propertyToAttribute(property, attribute, value); - } - - /** - * Sets a typed value to an HTML attribute on a node. - * - * Note this method is provided as backward-compatible legacy API - * only. It is not directly called by any Polymer features. - * - * @param {*} value Value to serialize. - * @param {string} attribute Attribute name to serialize to. - * @param {Element} node Element to set attribute to. - * @return {void} - * @override - */ - serializeValueToAttribute(value, attribute, node) { - this._valueToNodeAttribute(/** @type {Element} */ (node || this), value, attribute); - } - - /** - * Copies own properties (including accessor descriptors) from a source - * object to a target object. - * - * @param {Object} prototype Target object to copy properties to. - * @param {Object} api Source object to copy properties from. - * @return {Object} prototype object that was passed as first argument. - * @override - */ - extend(prototype, api) { - if (!(prototype && api)) { - return prototype || api; - } - let n$ = Object.getOwnPropertyNames(api); - for (let i=0, n; (i<n$.length) && (n=n$[i]); i++) { - let pd = Object.getOwnPropertyDescriptor(api, n); - if (pd) { - Object.defineProperty(prototype, n, pd); - } - } - return prototype; - } - - /** - * Copies props from a source object to a target object. - * - * Note, this method uses a simple `for...in` strategy for enumerating - * properties. To ensure only `ownProperties` are copied from source - * to target and that accessor implementations are copied, use `extend`. - * - * @param {!Object} target Target object to copy properties to. - * @param {!Object} source Source object to copy properties from. - * @return {!Object} Target object that was passed as first argument. - * @override - */ - mixin(target, source) { - for (let i in source) { - target[i] = source[i]; - } - return target; - } - - /** - * Sets the prototype of an object. - * - * Note this method is provided as backward-compatible legacy API - * only. It is not directly called by any Polymer features. - * @param {Object} object The object on which to set the prototype. - * @param {Object} prototype The prototype that will be set on the given - * `object`. - * @return {Object} Returns the given `object` with its prototype set - * to the given `prototype` object. - * @override - */ - chainObject(object, prototype) { - if (object && prototype && object !== prototype) { - object.__proto__ = prototype; - } - return object; - } - - /* **** Begin Template **** */ - - /** - * Calls `importNode` on the `content` of the `template` specified and - * returns a document fragment containing the imported content. - * - * @param {HTMLTemplateElement} template HTML template element to instance. - * @return {!DocumentFragment} Document fragment containing the imported - * template content. - * @override - * @suppress {missingProperties} go/missingfnprops - */ - instanceTemplate(template) { - let content = this.constructor._contentForTemplate(template); - let dom = /** @type {!DocumentFragment} */ - (document.importNode(content, true)); - return dom; - } - - /* **** Begin Events **** */ - - - - /** - * Dispatches a custom event with an optional detail value. - * - * @param {string} type Name of event type. - * @param {*=} detail Detail value containing event-specific - * payload. - * @param {{ bubbles: (boolean|undefined), cancelable: (boolean|undefined), - * composed: (boolean|undefined) }=} - * options Object specifying options. These may include: - * `bubbles` (boolean, defaults to `true`), - * `cancelable` (boolean, defaults to false), and - * `node` on which to fire the event (HTMLElement, defaults to `this`). - * @return {!Event} The new event that was fired. - * @override - */ - fire(type, detail, options) { - options = options || {}; - detail = (detail === null || detail === undefined) ? {} : detail; - let event = new Event(type, { - bubbles: options.bubbles === undefined ? true : options.bubbles, - cancelable: Boolean(options.cancelable), - composed: options.composed === undefined ? true: options.composed - }); - event.detail = detail; - let node = options.node || this; - wrap(node).dispatchEvent(event); - return event; - } - - /** - * Convenience method to add an event listener on a given element, - * late bound to a named method on this element. - * - * @param {?EventTarget} node Element to add event listener to. - * @param {string} eventName Name of event to listen for. - * @param {string} methodName Name of handler method on `this` to call. - * @return {void} - * @override - */ - listen(node, eventName, methodName) { - node = /** @type {!EventTarget} */ (node || this); - let hbl = this.__boundListeners || - (this.__boundListeners = new WeakMap()); - let bl = hbl.get(node); - if (!bl) { - bl = {}; - hbl.set(node, bl); - } - let key = eventName + methodName; - if (!bl[key]) { - bl[key] = this._addMethodEventListenerToNode( - /** @type {!Node} */ (node), eventName, methodName, this); - } - } - - /** - * Convenience method to remove an event listener from a given element, - * late bound to a named method on this element. - * - * @param {?EventTarget} node Element to remove event listener from. - * @param {string} eventName Name of event to stop listening to. - * @param {string} methodName Name of handler method on `this` to not call - anymore. - * @return {void} - * @override - */ - unlisten(node, eventName, methodName) { - node = /** @type {!EventTarget} */ (node || this); - let bl = this.__boundListeners && - this.__boundListeners.get(/** @type {!Element} */ (node)); - let key = eventName + methodName; - let handler = bl && bl[key]; - if (handler) { - this._removeEventListenerFromNode( - /** @type {!Node} */ (node), eventName, handler); - bl[key] = /** @type {?} */ (null); - } - } - - /** - * Override scrolling behavior to all direction, one direction, or none. - * - * Valid scroll directions: - * - 'all': scroll in any direction - * - 'x': scroll only in the 'x' direction - * - 'y': scroll only in the 'y' direction - * - 'none': disable scrolling for this node - * - * @param {string=} direction Direction to allow scrolling - * Defaults to `all`. - * @param {Element=} node Element to apply scroll direction setting. - * Defaults to `this`. - * @return {void} - * @override - */ - setScrollDirection(direction, node) { - setTouchAction( - /** @type {!Element} */ (node || this), - DIRECTION_MAP[direction] || 'auto'); - } - /* **** End Events **** */ - - /** - * Convenience method to run `querySelector` on this local DOM scope. - * - * This function calls `Polymer.dom(this.root).querySelector(slctr)`. - * - * @param {string} slctr Selector to run on this local DOM scope - * @return {Element} Element found by the selector, or null if not found. - * @override - */ - $$(slctr) { - // Note, no need to `wrap` this because root is always patched - return this.root.querySelector(slctr); - } - - /** - * Return the element whose local dom within which this element - * is contained. This is a shorthand for - * `this.getRootNode().host`. - * @this {Element} - */ - get domHost() { - let root = wrap(this).getRootNode(); - return (root instanceof DocumentFragment) ? /** @type {ShadowRoot} */ (root).host : root; - } - - /** - * Force this element to distribute its children to its local dom. - * This should not be necessary as of Polymer 2.0.2 and is provided only - * for backwards compatibility. - * @return {void} - * @override - */ - distributeContent() { - const thisEl = /** @type {Element} */ (this); - const domApi = /** @type {PolymerDomApi} */(dom(thisEl)); - if (window.ShadyDOM && domApi.shadowRoot) { - ShadyDOM.flush(); - } - } - - /** - * Returns a list of nodes that are the effective childNodes. The effective - * childNodes list is the same as the element's childNodes except that - * any `<content>` elements are replaced with the list of nodes distributed - * to the `<content>`, the result of its `getDistributedNodes` method. - * @return {!Array<!Node>} List of effective child nodes. - * @suppress {invalidCasts} LegacyElementMixin must be applied to an - * HTMLElement - * @override - */ - getEffectiveChildNodes() { - const thisEl = /** @type {Element} */ (this); - const domApi = /** @type {PolymerDomApi} */ (dom(thisEl)); - return domApi.getEffectiveChildNodes(); - } - - /** - * Returns a list of nodes distributed within this element that match - * `selector`. These can be dom children or elements distributed to - * children that are insertion points. - * @param {string} selector Selector to run. - * @return {!Array<!Node>} List of distributed elements that match selector. - * @suppress {invalidCasts} LegacyElementMixin must be applied to an - * HTMLElement - * @override - */ - queryDistributedElements(selector) { - const thisEl = /** @type {Element} */ (this); - const domApi = /** @type {PolymerDomApi} */ (dom(thisEl)); - return domApi.queryDistributedElements(selector); - } - - /** - * Returns a list of elements that are the effective children. The effective - * children list is the same as the element's children except that - * any `<content>` elements are replaced with the list of elements - * distributed to the `<content>`. - * - * @return {!Array<!Node>} List of effective children. - * @override - */ - getEffectiveChildren() { - let list = this.getEffectiveChildNodes(); - return list.filter(function(/** @type {!Node} */ n) { - return (n.nodeType === Node.ELEMENT_NODE); - }); - } - - /** - * Returns a string of text content that is the concatenation of the - * text content's of the element's effective childNodes (the elements - * returned by <a href="#getEffectiveChildNodes>getEffectiveChildNodes</a>. - * - * @return {string} List of effective children. - * @override - */ - getEffectiveTextContent() { - let cn = this.getEffectiveChildNodes(); - let tc = []; - for (let i=0, c; (c = cn[i]); i++) { - if (c.nodeType !== Node.COMMENT_NODE) { - tc.push(c.textContent); - } - } - return tc.join(''); - } - - /** - * Returns the first effective childNode within this element that - * match `selector`. These can be dom child nodes or elements distributed - * to children that are insertion points. - * @param {string} selector Selector to run. - * @return {Node} First effective child node that matches selector. - * @override - */ - queryEffectiveChildren(selector) { - let e$ = this.queryDistributedElements(selector); - return e$ && e$[0]; - } - - /** - * Returns a list of effective childNodes within this element that - * match `selector`. These can be dom child nodes or elements distributed - * to children that are insertion points. - * @param {string} selector Selector to run. - * @return {!Array<!Node>} List of effective child nodes that match - * selector. - * @override - */ - queryAllEffectiveChildren(selector) { - return this.queryDistributedElements(selector); - } - - /** - * Returns a list of nodes distributed to this element's `<slot>`. - * - * If this element contains more than one `<slot>` in its local DOM, - * an optional selector may be passed to choose the desired content. - * - * @param {string=} slctr CSS selector to choose the desired - * `<slot>`. Defaults to `content`. - * @return {!Array<!Node>} List of distributed nodes for the `<slot>`. - * @override - */ - getContentChildNodes(slctr) { - // Note, no need to `wrap` this because root is always - let content = this.root.querySelector(slctr || 'slot'); - return content ? - /** @type {PolymerDomApi} */ (dom(content)).getDistributedNodes() : - []; - } - - /** - * Returns a list of element children distributed to this element's - * `<slot>`. - * - * If this element contains more than one `<slot>` in its - * local DOM, an optional selector may be passed to choose the desired - * content. This method differs from `getContentChildNodes` in that only - * elements are returned. - * - * @param {string=} slctr CSS selector to choose the desired - * `<content>`. Defaults to `content`. - * @return {!Array<!HTMLElement>} List of distributed nodes for the - * `<slot>`. - * @suppress {invalidCasts} - * @override - */ - getContentChildren(slctr) { - let children = /** @type {!Array<!HTMLElement>} */(this.getContentChildNodes(slctr).filter(function(n) { - return (n.nodeType === Node.ELEMENT_NODE); - })); - return children; - } - - /** - * Checks whether an element is in this element's light DOM tree. - * - * @param {?Node} node The element to be checked. - * @return {boolean} true if node is in this element's light DOM tree. - * @suppress {invalidCasts} LegacyElementMixin must be applied to an - * HTMLElement - * @override - */ - isLightDescendant(node) { - const thisNode = /** @type {Node} */ (this); - return thisNode !== node && wrap(thisNode).contains(node) && - wrap(thisNode).getRootNode() === wrap(node).getRootNode(); - } - - /** - * Checks whether an element is in this element's local DOM tree. - * - * @param {!Element} node The element to be checked. - * @return {boolean} true if node is in this element's local DOM tree. - * @override - */ - isLocalDescendant(node) { - return this.root === wrap(node).getRootNode(); - } - - /** - * No-op for backwards compatibility. This should now be handled by - * ShadyCss library. - * @param {*} container Unused - * @param {*} shouldObserve Unused - * @return {void} - * @override - */ - scopeSubtree(container, shouldObserve) { // eslint-disable-line no-unused-vars - } - - /** - * Returns the computed style value for the given property. - * @param {string} property The css property name. - * @return {string} Returns the computed css property value for the given - * `property`. - * @suppress {invalidCasts} LegacyElementMixin must be applied to an - * HTMLElement - * @override - */ - getComputedStyleValue(property) { - return styleInterface.getComputedStyleValue(/** @type {!Element} */(this), property); - } - - // debounce - - /** - * Call `debounce` to collapse multiple requests for a named task into - * one invocation which is made after the wait time has elapsed with - * no new request. If no wait time is given, the callback will be called - * at microtask timing (guaranteed before paint). - * - * debouncedClickAction(e) { - * // will not call `processClick` more than once per 100ms - * this.debounce('click', function() { - * this.processClick(); - * } 100); - * } - * - * @param {string} jobName String to identify the debounce job. - * @param {function():void} callback Function that is called (with `this` - * context) when the wait time elapses. - * @param {number=} wait Optional wait time in milliseconds (ms) after the - * last signal that must elapse before invoking `callback` - * @return {!Object} Returns a debouncer object on which exists the - * following methods: `isActive()` returns true if the debouncer is - * active; `cancel()` cancels the debouncer if it is active; - * `flush()` immediately invokes the debounced callback if the debouncer - * is active. - * @override - */ - debounce(jobName, callback, wait) { - this._debouncers = this._debouncers || {}; - return this._debouncers[jobName] = Debouncer.debounce( - this._debouncers[jobName] - , wait > 0 ? timeOut.after(wait) : microTask - , callback.bind(this)); - } - - /** - * Returns whether a named debouncer is active. - * - * @param {string} jobName The name of the debouncer started with `debounce` - * @return {boolean} Whether the debouncer is active (has not yet fired). - * @override - */ - isDebouncerActive(jobName) { - this._debouncers = this._debouncers || {}; - let debouncer = this._debouncers[jobName]; - return !!(debouncer && debouncer.isActive()); - } - - /** - * Immediately calls the debouncer `callback` and inactivates it. - * - * @param {string} jobName The name of the debouncer started with `debounce` - * @return {void} - * @override - */ - flushDebouncer(jobName) { - this._debouncers = this._debouncers || {}; - let debouncer = this._debouncers[jobName]; - if (debouncer) { - debouncer.flush(); - } - } - - /** - * Cancels an active debouncer. The `callback` will not be called. - * - * @param {string} jobName The name of the debouncer started with `debounce` - * @return {void} - * @override - */ - cancelDebouncer(jobName) { - this._debouncers = this._debouncers || {}; - let debouncer = this._debouncers[jobName]; - if (debouncer) { - debouncer.cancel(); - } - } - - /** - * Runs a callback function asynchronously. - * - * By default (if no waitTime is specified), async callbacks are run at - * microtask timing, which will occur before paint. - * - * @param {!Function} callback The callback function to run, bound to - * `this`. - * @param {number=} waitTime Time to wait before calling the - * `callback`. If unspecified or 0, the callback will be run at microtask - * timing (before paint). - * @return {number} Handle that may be used to cancel the async job. - * @override - */ - async(callback, waitTime) { - return waitTime > 0 ? timeOut.run(callback.bind(this), waitTime) : - ~microTask.run(callback.bind(this)); - } - - /** - * Cancels an async operation started with `async`. - * - * @param {number} handle Handle returned from original `async` call to - * cancel. - * @return {void} - * @override - */ - cancelAsync(handle) { - handle < 0 ? microTask.cancel(~handle) : - timeOut.cancel(handle); - } - - // other - - /** - * Convenience method for creating an element and configuring it. - * - * @param {string} tag HTML element tag to create. - * @param {Object=} props Object of properties to configure on the - * instance. - * @return {!Element} Newly created and configured element. - * @override - */ - create(tag, props) { - let elt = document.createElement(tag); - if (props) { - if (elt.setProperties) { - elt.setProperties(props); - } else { - for (let n in props) { - elt[n] = props[n]; - } - } - } - return elt; - } - - /** - * Polyfill for Element.prototype.matches, which is sometimes still - * prefixed. - * - * @param {string} selector Selector to test. - * @param {!Element=} node Element to test the selector against. - * @return {boolean} Whether the element matches the selector. - * @override - */ - elementMatches(selector, node) { - return matchesSelector( (node || this), selector); - } - - /** - * Toggles an HTML attribute on or off. - * - * @param {string} name HTML attribute name - * @param {boolean=} bool Boolean to force the attribute on or off. - * When unspecified, the state of the attribute will be reversed. - * @return {boolean} true if the attribute now exists - * @override - */ - toggleAttribute(name, bool) { - let node = /** @type {Element} */(this); - if (arguments.length === 3) { - node = /** @type {Element} */(arguments[2]); - } - if (arguments.length == 1) { - bool = !node.hasAttribute(name); - } - if (bool) { - wrap(node).setAttribute(name, ''); - return true; - } else { - wrap(node).removeAttribute(name); - return false; - } - } - - - /** - * Toggles a CSS class on or off. - * - * @param {string} name CSS class name - * @param {boolean=} bool Boolean to force the class on or off. - * When unspecified, the state of the class will be reversed. - * @param {Element=} node Node to target. Defaults to `this`. - * @return {void} - * @override - */ - toggleClass(name, bool, node) { - node = /** @type {Element} */ (node || this); - if (arguments.length == 1) { - bool = !node.classList.contains(name); - } - if (bool) { - node.classList.add(name); - } else { - node.classList.remove(name); - } - } - - /** - * Cross-platform helper for setting an element's CSS `transform` property. - * - * @param {string} transformText Transform setting. - * @param {Element=} node Element to apply the transform to. - * Defaults to `this` - * @return {void} - * @override - */ - transform(transformText, node) { - node = /** @type {Element} */ (node || this); - node.style.webkitTransform = transformText; - node.style.transform = transformText; - } - - /** - * Cross-platform helper for setting an element's CSS `translate3d` - * property. - * - * @param {number} x X offset. - * @param {number} y Y offset. - * @param {number} z Z offset. - * @param {Element=} node Element to apply the transform to. - * Defaults to `this`. - * @return {void} - * @override - */ - translate3d(x, y, z, node) { - node = /** @type {Element} */ (node || this); - this.transform('translate3d(' + x + ',' + y + ',' + z + ')', node); - } - - /** - * Removes an item from an array, if it exists. - * - * If the array is specified by path, a change notification is - * generated, so that observers, data bindings and computed - * properties watching that path can update. - * - * If the array is passed directly, **no change - * notification is generated**. - * - * @param {string | !Array<number|string>} arrayOrPath Path to array from - * which to remove the item - * (or the array itself). - * @param {*} item Item to remove. - * @return {Array} Array containing item removed. - * @override - */ - arrayDelete(arrayOrPath, item) { - let index; - if (Array.isArray(arrayOrPath)) { - index = arrayOrPath.indexOf(item); - if (index >= 0) { - return arrayOrPath.splice(index, 1); - } - } else { - let arr = get(this, arrayOrPath); - index = arr.indexOf(item); - if (index >= 0) { - return this.splice(arrayOrPath, index, 1); - } - } - return null; - } - - // logging - - /** - * Facades `console.log`/`warn`/`error` as override point. - * - * @param {string} level One of 'log', 'warn', 'error' - * @param {Array} args Array of strings or objects to log - * @return {void} - * @override - */ - _logger(level, args) { - // accept ['foo', 'bar'] and [['foo', 'bar']] - if (Array.isArray(args) && args.length === 1 && Array.isArray(args[0])) { - args = args[0]; - } - switch(level) { - case 'log': - case 'warn': - case 'error': - console[level](...args); - } - } - - /** - * Facades `console.log` as an override point. - * - * @param {...*} args Array of strings or objects to log - * @return {void} - * @override - */ - _log(...args) { - this._logger('log', args); - } - - /** - * Facades `console.warn` as an override point. - * - * @param {...*} args Array of strings or objects to log - * @return {void} - * @override - */ - _warn(...args) { - this._logger('warn', args); - } - - /** - * Facades `console.error` as an override point. - * - * @param {...*} args Array of strings or objects to log - * @return {void} - * @override - */ - _error(...args) { - this._logger('error', args); - } - - /** - * Formats a message using the element type an a method name. - * - * @param {string} methodName Method name to associate with message - * @param {...*} args Array of strings or objects to log - * @return {Array} Array with formatting information for `console` - * logging. - * @override - */ - _logf(methodName, ...args) { - return ['[%s::%s]', this.is, methodName, ...args]; - } - - } - - LegacyElement.prototype.is = ''; - - return LegacyElement; -});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/mutable-data-behavior.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/mutable-data-behavior.js deleted file mode 100644 index ee23666..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/mutable-data-behavior.js +++ /dev/null
@@ -1,150 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import { MutableData } from '../mixins/mutable-data.js'; - -let mutablePropertyChange; -/** @suppress {missingProperties} */ -(() => { - mutablePropertyChange = MutableData._mutablePropertyChange; -})(); - -/** - * Legacy element behavior to skip strict dirty-checking for objects and arrays, - * (always consider them to be "dirty") for use on legacy API Polymer elements. - * - * By default, `Polymer.PropertyEffects` performs strict dirty checking on - * objects, which means that any deep modifications to an object or array will - * not be propagated unless "immutable" data patterns are used (i.e. all object - * references from the root to the mutation were changed). - * - * Polymer also provides a proprietary data mutation and path notification API - * (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient - * mutation and notification of deep changes in an object graph to all elements - * bound to the same object graph. - * - * In cases where neither immutable patterns nor the data mutation API can be - * used, applying this mixin will cause Polymer to skip dirty checking for - * objects and arrays (always consider them to be "dirty"). This allows a - * user to make a deep modification to a bound object graph, and then either - * simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath` - * (e.g. `this.notifyPath('items')`) to update the tree. Note that all - * elements that wish to be updated based on deep mutations must apply this - * mixin or otherwise skip strict dirty checking for objects/arrays. - * Specifically, any elements in the binding tree between the source of a - * mutation and the consumption of it must apply this behavior or enable the - * `Polymer.OptionalMutableDataBehavior`. - * - * In order to make the dirty check strategy configurable, see - * `Polymer.OptionalMutableDataBehavior`. - * - * Note, the performance characteristics of propagating large object graphs - * will be worse as opposed to using strict dirty checking with immutable - * patterns or Polymer's path notification API. - * - * @polymerBehavior - * @summary Behavior to skip strict dirty-checking for objects and - * arrays - */ -export const MutableDataBehavior = { - - /** - * Overrides `Polymer.PropertyEffects` to provide option for skipping - * strict equality checking for Objects and Arrays. - * - * This method pulls the value to dirty check against from the `__dataTemp` - * cache (rather than the normal `__data` cache) for Objects. Since the temp - * cache is cleared at the end of a turn, this implementation allows - * side-effects of deep object changes to be processed by re-setting the - * same object (using the temp cache as an in-turn backstop to prevent - * cycles due to 2-way notification). - * - * @param {string} property Property name - * @param {*} value New property value - * @param {*} old Previous property value - * @return {boolean} Whether the property should be considered a change - * @protected - */ - _shouldPropertyChange(property, value, old) { - return mutablePropertyChange(this, property, value, old, true); - } -}; - -/** - * Legacy element behavior to add the optional ability to skip strict - * dirty-checking for objects and arrays (always consider them to be - * "dirty") by setting a `mutable-data` attribute on an element instance. - * - * By default, `Polymer.PropertyEffects` performs strict dirty checking on - * objects, which means that any deep modifications to an object or array will - * not be propagated unless "immutable" data patterns are used (i.e. all object - * references from the root to the mutation were changed). - * - * Polymer also provides a proprietary data mutation and path notification API - * (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient - * mutation and notification of deep changes in an object graph to all elements - * bound to the same object graph. - * - * In cases where neither immutable patterns nor the data mutation API can be - * used, applying this mixin will allow Polymer to skip dirty checking for - * objects and arrays (always consider them to be "dirty"). This allows a - * user to make a deep modification to a bound object graph, and then either - * simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath` - * (e.g. `this.notifyPath('items')`) to update the tree. Note that all - * elements that wish to be updated based on deep mutations must apply this - * mixin or otherwise skip strict dirty checking for objects/arrays. - * Specifically, any elements in the binding tree between the source of a - * mutation and the consumption of it must enable this behavior or apply the - * `Polymer.OptionalMutableDataBehavior`. - * - * While this behavior adds the ability to forgo Object/Array dirty checking, - * the `mutableData` flag defaults to false and must be set on the instance. - * - * Note, the performance characteristics of propagating large object graphs - * will be worse by relying on `mutableData: true` as opposed to using - * strict dirty checking with immutable patterns or Polymer's path notification - * API. - * - * @polymerBehavior - * @summary Behavior to optionally skip strict dirty-checking for objects and - * arrays - */ -export const OptionalMutableDataBehavior = { - - properties: { - /** - * Instance-level flag for configuring the dirty-checking strategy - * for this element. When true, Objects and Arrays will skip dirty - * checking, otherwise strict equality checking will be used. - */ - mutableData: Boolean - }, - - /** - * Overrides `Polymer.PropertyEffects` to skip strict equality checking - * for Objects and Arrays. - * - * Pulls the value to dirty check against from the `__dataTemp` cache - * (rather than the normal `__data` cache) for Objects. Since the temp - * cache is cleared at the end of a turn, this implementation allows - * side-effects of deep object changes to be processed by re-setting the - * same object (using the temp cache as an in-turn backstop to prevent - * cycles due to 2-way notification). - * - * @param {string} property Property name - * @param {*} value New property value - * @param {*} old Previous property value - * @return {boolean} Whether the property should be considered a change - * @this {this} - * @protected - */ - _shouldPropertyChange(property, value, old) { - return mutablePropertyChange(this, property, value, old, this.mutableData); - } -};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer-fn.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer-fn.js deleted file mode 100644 index de019be6..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer-fn.js +++ /dev/null
@@ -1,48 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import { Class } from './class.js'; - -import '../utils/boot.js'; - -/** - * Legacy class factory and registration helper for defining Polymer - * elements. - * - * This method is equivalent to - * - * import {Class} from '@polymer/polymer/lib/legacy/class.js'; - * customElements.define(info.is, Class(info)); - * - * See `Class` for details on valid legacy metadata format for `info`. - * - * @global - * @override - * @function - * @param {!PolymerInit} info Object containing Polymer metadata and functions - * to become class methods. - * @return {function(new: HTMLElement)} Generated class - * @suppress {duplicate, invalidCasts, checkTypes} - */ -const Polymer = function(info) { - // if input is a `class` (aka a function with a prototype), use the prototype - // remember that the `constructor` will never be called - let klass; - if (typeof info === 'function') { - klass = info; - } else { - klass = Polymer.Class(info); - } - customElements.define(klass.is, /** @type {!HTMLElement} */(klass)); - return klass; -}; - -Polymer.Class = Class; - -export { Polymer }; \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer.dom.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer.dom.js deleted file mode 100644 index 6d7f0eb6..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer.dom.js +++ /dev/null
@@ -1,482 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import '../utils/boot.js'; -import { wrap } from '../utils/wrap.js'; -import '../utils/settings.js'; -import { FlattenedNodesObserver } from '../utils/flattened-nodes-observer.js'; -export { flush, enqueueDebouncer as addDebouncer } from '../utils/flush.js'; -/* eslint-disable no-unused-vars */ -import { Debouncer } from '../utils/debounce.js'; // used in type annotations -/* eslint-enable no-unused-vars */ - -const p = Element.prototype; -/** - * @const {function(this:Node, string): boolean} - */ -const normalizedMatchesSelector = p.matches || p.matchesSelector || - p.mozMatchesSelector || p.msMatchesSelector || - p.oMatchesSelector || p.webkitMatchesSelector; - -/** - * Cross-platform `element.matches` shim. - * - * @function matchesSelector - * @param {!Node} node Node to check selector against - * @param {string} selector Selector to match - * @return {boolean} True if node matched selector - */ -export const matchesSelector = function(node, selector) { - return normalizedMatchesSelector.call(node, selector); -}; - -/** - * Node API wrapper class returned from `Polymer.dom.(target)` when - * `target` is a `Node`. - * @implements {PolymerDomApi} - * @unrestricted - */ -class DomApiNative { - - /** - * @param {Node} node Node for which to create a Polymer.dom helper object. - */ - constructor(node) { - this.node = node; - } - - /** - * Returns an instance of `FlattenedNodesObserver` that - * listens for node changes on this element. - * - * @param {function(this:HTMLElement, { target: !HTMLElement, addedNodes: !Array<!Element>, removedNodes: !Array<!Element> }):void} callback Called when direct or distributed children - * of this element changes - * @return {!PolymerDomApi.ObserveHandle} Observer instance - * @override - */ - observeNodes(callback) { - return new FlattenedNodesObserver( - /** @type {!HTMLElement} */(this.node), callback); - } - - /** - * Disconnects an observer previously created via `observeNodes` - * - * @param {!PolymerDomApi.ObserveHandle} observerHandle Observer instance - * to disconnect. - * @return {void} - * @override - */ - unobserveNodes(observerHandle) { - observerHandle.disconnect(); - } - - /** - * Provided as a backwards-compatible API only. This method does nothing. - * @return {void} - */ - notifyObserver() {} - - /** - * Returns true if the provided node is contained with this element's - * light-DOM children or shadow root, including any nested shadow roots - * of children therein. - * - * @param {Node} node Node to test - * @return {boolean} Returns true if the given `node` is contained within - * this element's light or shadow DOM. - * @override - */ - deepContains(node) { - if (wrap(this.node).contains(node)) { - return true; - } - let n = node; - let doc = node.ownerDocument; - // walk from node to `this` or `document` - while (n && n !== doc && n !== this.node) { - // use logical parentnode, or native ShadowRoot host - n = wrap(n).parentNode || wrap(n).host; - } - return n === this.node; - } - - /** - * Returns the root node of this node. Equivalent to `getRootNode()`. - * - * @return {Node} Top most element in the dom tree in which the node - * exists. If the node is connected to a document this is either a - * shadowRoot or the document; otherwise, it may be the node - * itself or a node or document fragment containing it. - * @override - */ - getOwnerRoot() { - return wrap(this.node).getRootNode(); - } - - /** - * For slot elements, returns the nodes assigned to the slot; otherwise - * an empty array. It is equivalent to `<slot>.addignedNodes({flatten:true})`. - * - * @return {!Array<!Node>} Array of assigned nodes - * @override - */ - getDistributedNodes() { - return (this.node.localName === 'slot') ? - wrap(this.node).assignedNodes({flatten: true}) : - []; - } - - /** - * Returns an array of all slots this element was distributed to. - * - * @return {!Array<!HTMLSlotElement>} Description - * @override - */ - getDestinationInsertionPoints() { - let ip$ = []; - let n = wrap(this.node).assignedSlot; - while (n) { - ip$.push(n); - n = wrap(n).assignedSlot; - } - return ip$; - } - - /** - * Calls `importNode` on the `ownerDocument` for this node. - * - * @param {!Node} node Node to import - * @param {boolean} deep True if the node should be cloned deeply during - * import - * @return {Node} Clone of given node imported to this owner document - */ - importNode(node, deep) { - let doc = this.node instanceof Document ? this.node : - this.node.ownerDocument; - return wrap(doc).importNode(node, deep); - } - - /** - * @return {!Array<!Node>} Returns a flattened list of all child nodes and - * nodes assigned to child slots. - * @override - */ - getEffectiveChildNodes() { - return FlattenedNodesObserver.getFlattenedNodes( - /** @type {!HTMLElement} */ (this.node)); - } - - /** - * Returns a filtered list of flattened child elements for this element based - * on the given selector. - * - * @param {string} selector Selector to filter nodes against - * @return {!Array<!HTMLElement>} List of flattened child elements - * @override - */ - queryDistributedElements(selector) { - let c$ = this.getEffectiveChildNodes(); - let list = []; - for (let i=0, l=c$.length, c; (i<l) && (c=c$[i]); i++) { - if ((c.nodeType === Node.ELEMENT_NODE) && - matchesSelector(c, selector)) { - list.push(c); - } - } - return list; - } - - /** - * For shadow roots, returns the currently focused element within this - * shadow root. - * - * return {Node|undefined} Currently focused element - * @override - */ - get activeElement() { - let node = this.node; - return node._activeElement !== undefined ? node._activeElement : node.activeElement; - } -} - -function forwardMethods(proto, methods) { - for (let i=0; i < methods.length; i++) { - let method = methods[i]; - /* eslint-disable valid-jsdoc */ - proto[method] = /** @this {DomApiNative} */ function() { - return this.node[method].apply(this.node, arguments); - }; - /* eslint-enable */ - } -} - -function forwardReadOnlyProperties(proto, properties) { - for (let i=0; i < properties.length; i++) { - let name = properties[i]; - Object.defineProperty(proto, name, { - get: function() { - const domApi = /** @type {DomApiNative} */(this); - return domApi.node[name]; - }, - configurable: true - }); - } -} - -function forwardProperties(proto, properties) { - for (let i=0; i < properties.length; i++) { - let name = properties[i]; - Object.defineProperty(proto, name, { - /** - * @this {DomApiNative} - * @return {*} . - */ - get: function() { - return this.node[name]; - }, - /** - * @this {DomApiNative} - * @param {*} value . - */ - set: function(value) { - this.node[name] = value; - }, - configurable: true - }); - } -} - - -/** - * Event API wrapper class returned from `dom.(target)` when - * `target` is an `Event`. - */ -export class EventApi { - constructor(event) { - this.event = event; - } - - /** - * Returns the first node on the `composedPath` of this event. - * - * @return {!EventTarget} The node this event was dispatched to - */ - get rootTarget() { - return this.path[0]; - } - - /** - * Returns the local (re-targeted) target for this event. - * - * @return {!EventTarget} The local (re-targeted) target for this event. - */ - get localTarget() { - return this.event.target; - } - - /** - * Returns the `composedPath` for this event. - * @return {!Array<!EventTarget>} The nodes this event propagated through - */ - get path() { - return this.event.composedPath(); - } -} - -/** - * @function - * @param {boolean=} deep - * @return {!Node} - */ -DomApiNative.prototype.cloneNode; -/** - * @function - * @param {!Node} node - * @return {!Node} - */ -DomApiNative.prototype.appendChild; -/** - * @function - * @param {!Node} newChild - * @param {Node} refChild - * @return {!Node} - */ -DomApiNative.prototype.insertBefore; -/** - * @function - * @param {!Node} node - * @return {!Node} - */ -DomApiNative.prototype.removeChild; -/** - * @function - * @param {!Node} oldChild - * @param {!Node} newChild - * @return {!Node} - */ -DomApiNative.prototype.replaceChild; -/** - * @function - * @param {string} name - * @param {string} value - * @return {void} - */ -DomApiNative.prototype.setAttribute; -/** - * @function - * @param {string} name - * @return {void} - */ -DomApiNative.prototype.removeAttribute; -/** - * @function - * @param {string} selector - * @return {?Element} - */ -DomApiNative.prototype.querySelector; -/** - * @function - * @param {string} selector - * @return {!NodeList<!Element>} - */ -DomApiNative.prototype.querySelectorAll; - -/** @type {?Node} */ -DomApiNative.prototype.parentNode; -/** @type {?Node} */ -DomApiNative.prototype.firstChild; -/** @type {?Node} */ -DomApiNative.prototype.lastChild; -/** @type {?Node} */ -DomApiNative.prototype.nextSibling; -/** @type {?Node} */ -DomApiNative.prototype.previousSibling; -/** @type {?HTMLElement} */ -DomApiNative.prototype.firstElementChild; -/** @type {?HTMLElement} */ -DomApiNative.prototype.lastElementChild; -/** @type {?HTMLElement} */ -DomApiNative.prototype.nextElementSibling; -/** @type {?HTMLElement} */ -DomApiNative.prototype.previousElementSibling; -/** @type {!Array<!Node>} */ -DomApiNative.prototype.childNodes; -/** @type {!Array<!HTMLElement>} */ -DomApiNative.prototype.children; -/** @type {?DOMTokenList} */ -DomApiNative.prototype.classList; - -/** @type {string} */ -DomApiNative.prototype.textContent; -/** @type {string} */ -DomApiNative.prototype.innerHTML; - -let DomApiImpl = DomApiNative; - -if (window['ShadyDOM'] && window['ShadyDOM']['inUse'] && window['ShadyDOM']['noPatch'] && window['ShadyDOM']['Wrapper']) { - - /** - * @private - * @extends {HTMLElement} - */ - class Wrapper extends window['ShadyDOM']['Wrapper'] {} - - // copy bespoke API onto wrapper - Object.getOwnPropertyNames(DomApiNative.prototype).forEach((prop) => { - if (prop != 'activeElement') { - Wrapper.prototype[prop] = DomApiNative.prototype[prop]; - } - }); - - // Note, `classList` is here only for legacy compatibility since it does not - // trigger distribution in v1 Shadow DOM. - forwardReadOnlyProperties(Wrapper.prototype, [ - 'classList' - ]); - - DomApiImpl = Wrapper; - - Object.defineProperties(EventApi.prototype, { - - localTarget: { - get() { - return this.event.currentTarget; - }, - configurable: true - }, - - path: { - get() { - return window['ShadyDOM']['composedPath'](this.event); - }, - configurable: true - } - }); - -} else { - - // Methods that can provoke distribution or must return the logical, not - // composed tree. - forwardMethods(DomApiNative.prototype, [ - 'cloneNode', 'appendChild', 'insertBefore', 'removeChild', - 'replaceChild', 'setAttribute', 'removeAttribute', - 'querySelector', 'querySelectorAll' - ]); - - // Properties that should return the logical, not composed tree. Note, `classList` - // is here only for legacy compatibility since it does not trigger distribution - // in v1 Shadow DOM. - forwardReadOnlyProperties(DomApiNative.prototype, [ - 'parentNode', 'firstChild', 'lastChild', - 'nextSibling', 'previousSibling', 'firstElementChild', - 'lastElementChild', 'nextElementSibling', 'previousElementSibling', - 'childNodes', 'children', 'classList' - ]); - - forwardProperties(DomApiNative.prototype, [ - 'textContent', 'innerHTML' - ]); -} - -export const DomApi = DomApiImpl; - -/** - * Legacy DOM and Event manipulation API wrapper factory used to abstract - * differences between native Shadow DOM and "Shady DOM" when polyfilling on - * older browsers. - * - * Note that in Polymer 2.x use of `Polymer.dom` is no longer required and - * in the majority of cases simply facades directly to the standard native - * API. - * - * @summary Legacy DOM and Event manipulation API wrapper factory used to - * abstract differences between native Shadow DOM and "Shady DOM." - * @param {(Node|Event|DomApiNative|EventApi)=} obj Node or event to operate on - * @return {!DomApiNative|!EventApi} Wrapper providing either node API or event API - */ -export const dom = function(obj) { - obj = obj || document; - if (obj instanceof DomApiImpl) { - return /** @type {!DomApi} */(obj); - } - if (obj instanceof EventApi) { - return /** @type {!EventApi} */(obj); - } - let helper = obj['__domApi']; - if (!helper) { - if (obj instanceof Event) { - helper = new EventApi(obj); - } else { - helper = new DomApiImpl(/** @type {Node} */(obj)); - } - obj['__domApi'] = helper; - } - return helper; -};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/templatizer-behavior.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/templatizer-behavior.js deleted file mode 100644 index 2dfbbc3..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/templatizer-behavior.js +++ /dev/null
@@ -1,142 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import { TemplateInstanceBase, templatize, modelForElement } from '../utils/templatize.js'; // eslint-disable-line no-unused-vars - -/** - * @typedef {{ - * _templatizerTemplate: HTMLTemplateElement, - * _parentModel: boolean, - * _instanceProps: Object, - * _forwardHostPropV2: Function, - * _notifyInstancePropV2: Function, - * ctor: TemplateInstanceBase - * }} - */ -let TemplatizerUser; // eslint-disable-line - -/** - * The `Templatizer` behavior adds methods to generate instances of - * templates that are each managed by an anonymous `PropertyEffects` - * instance where data-bindings in the stamped template content are bound to - * accessors on itself. - * - * This behavior is provided in Polymer 2.x-3.x as a hybrid-element convenience - * only. For non-hybrid usage, the `Templatize` library - * should be used instead. - * - * Example: - * - * import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js'; - * // Get a template from somewhere, e.g. light DOM - * let template = this.querySelector('template'); - * // Prepare the template - * this.templatize(template); - * // Instance the template with an initial data model - * let instance = this.stamp({myProp: 'initial'}); - * // Insert the instance's DOM somewhere, e.g. light DOM - * dom(this).appendChild(instance.root); - * // Changing a property on the instance will propagate to bindings - * // in the template - * instance.myProp = 'new value'; - * - * Users of `Templatizer` may need to implement the following abstract - * API's to determine how properties and paths from the host should be - * forwarded into to instances: - * - * _forwardHostPropV2: function(prop, value) - * - * Likewise, users may implement these additional abstract API's to determine - * how instance-specific properties that change on the instance should be - * forwarded out to the host, if necessary. - * - * _notifyInstancePropV2: function(inst, prop, value) - * - * In order to determine which properties are instance-specific and require - * custom notification via `_notifyInstanceProp`, define an `_instanceProps` - * object containing keys for each instance prop, for example: - * - * _instanceProps: { - * item: true, - * index: true - * } - * - * Any properties used in the template that are not defined in _instanceProp - * will be forwarded out to the Templatize `owner` automatically. - * - * Users may also implement the following abstract function to show or - * hide any DOM generated using `stamp`: - * - * _showHideChildren: function(shouldHide) - * - * Note that some callbacks are suffixed with `V2` in the Polymer 2.x behavior - * as the implementations will need to differ from the callbacks required - * by the 1.x Templatizer API due to changes in the `TemplateInstance` API - * between versions 1.x and 2.x. - * - * @polymerBehavior - */ -export const Templatizer = { - - /** - * Generates an anonymous `TemplateInstance` class (stored as `this.ctor`) - * for the provided template. This method should be called once per - * template to prepare an element for stamping the template, followed - * by `stamp` to create new instances of the template. - * - * @param {!HTMLTemplateElement} template Template to prepare - * @param {boolean=} mutableData When `true`, the generated class will skip - * strict dirty-checking for objects and arrays (always consider them to - * be "dirty"). Defaults to false. - * @return {void} - * @this {TemplatizerUser} - */ - templatize(template, mutableData) { - this._templatizerTemplate = template; - this.ctor = templatize(template, this, { - mutableData: Boolean(mutableData), - parentModel: this._parentModel, - instanceProps: this._instanceProps, - forwardHostProp: this._forwardHostPropV2, - notifyInstanceProp: this._notifyInstancePropV2 - }); - }, - - /** - * Creates an instance of the template prepared by `templatize`. The object - * returned is an instance of the anonymous class generated by `templatize` - * whose `root` property is a document fragment containing newly cloned - * template content, and which has property accessors corresponding to - * properties referenced in template bindings. - * - * @param {Object=} model Object containing initial property values to - * populate into the template bindings. - * @return {TemplateInstanceBase} Returns the created instance of - * the template prepared by `templatize`. - * @this {TemplatizerUser} - */ - stamp(model) { - return new this.ctor(model); - }, - - /** - * Returns the template "model" (`TemplateInstance`) associated with - * a given element, which serves as the binding scope for the template - * instance the element is contained in. A template model should be used - * to manipulate data associated with this template instance. - * - * @param {HTMLElement} el Element for which to return a template model. - * @return {TemplateInstanceBase} Model representing the binding scope for - * the element. - * @this {TemplatizerUser} - */ - modelForElement(el) { - return modelForElement(this._templatizerTemplate, el); - } -};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/dir-mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/dir-mixin.js deleted file mode 100644 index 3237511..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/dir-mixin.js +++ /dev/null
@@ -1,195 +0,0 @@ -/** - * @fileoverview - * @suppress {checkPrototypalTypes} - * @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt The complete set of authors may be found - * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may - * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by - * Google as part of the polymer project is also subject to an additional IP - * rights grant found at http://polymer.github.io/PATENTS.txt - */ -import { PropertyAccessors } from './property-accessors.js'; - -import { dedupingMixin } from '../utils/mixin.js'; - -const HOST_DIR = /:host\(:dir\((ltr|rtl)\)\)/g; -const HOST_DIR_REPLACMENT = ':host([dir="$1"])'; - -const EL_DIR = /([\s\w-#\.\[\]\*]*):dir\((ltr|rtl)\)/g; -const EL_DIR_REPLACMENT = ':host([dir="$2"]) $1'; - -const DIR_CHECK = /:dir\((?:ltr|rtl)\)/; - -const SHIM_SHADOW = Boolean(window['ShadyDOM'] && window['ShadyDOM']['inUse']); - -/** - * @type {!Array<!Polymer_DirMixin>} - */ -const DIR_INSTANCES = []; - -/** @type {?MutationObserver} */ -let observer = null; - -let DOCUMENT_DIR = ''; - -function getRTL() { - DOCUMENT_DIR = document.documentElement.getAttribute('dir'); -} - -/** - * @param {!Polymer_DirMixin} instance Instance to set RTL status on - */ -function setRTL(instance) { - if (!instance.__autoDirOptOut) { - const el = /** @type {!HTMLElement} */(instance); - el.setAttribute('dir', DOCUMENT_DIR); - } -} - -function updateDirection() { - getRTL(); - DOCUMENT_DIR = document.documentElement.getAttribute('dir'); - for (let i = 0; i < DIR_INSTANCES.length; i++) { - setRTL(DIR_INSTANCES[i]); - } -} - -function takeRecords() { - if (observer && observer.takeRecords().length) { - updateDirection(); - } -} - -/** - * Element class mixin that allows elements to use the `:dir` CSS Selector to - * have text direction specific styling. - * - * With this mixin, any stylesheet provided in the template will transform - * `:dir` into `:host([dir])` and sync direction with the page via the - * element's `dir` attribute. - * - * Elements can opt out of the global page text direction by setting the `dir` - * attribute directly in `ready()` or in HTML. - * - * Caveats: - * - Applications must set `<html dir="ltr">` or `<html dir="rtl">` to sync - * direction - * - Automatic left-to-right or right-to-left styling is sync'd with the - * `<html>` element only. - * - Changing `dir` at runtime is supported. - * - Opting out of the global direction styling is permanent - * - * @mixinFunction - * @polymer - * @appliesMixin PropertyAccessors - */ -export const DirMixin = dedupingMixin((base) => { - - if (!SHIM_SHADOW) { - if (!observer) { - getRTL(); - observer = new MutationObserver(updateDirection); - observer.observe(document.documentElement, {attributes: true, attributeFilter: ['dir']}); - } - } - - /** - * @constructor - * @implements {Polymer_PropertyAccessors} - * @private - */ - const elementBase = PropertyAccessors(base); - - /** - * @polymer - * @mixinClass - * @implements {Polymer_DirMixin} - */ - class Dir extends elementBase { - - /** - * @param {string} cssText . - * @param {string} baseURI . - * @return {string} . - * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do - */ - static _processStyleText(cssText, baseURI) { - cssText = super._processStyleText(cssText, baseURI); - if (!SHIM_SHADOW && DIR_CHECK.test(cssText)) { - cssText = this._replaceDirInCssText(cssText); - this.__activateDir = true; - } - return cssText; - } - - /** - * Replace `:dir` in the given CSS text - * - * @param {string} text CSS text to replace DIR - * @return {string} Modified CSS - */ - static _replaceDirInCssText(text) { - let replacedText = text; - replacedText = replacedText.replace(HOST_DIR, HOST_DIR_REPLACMENT); - replacedText = replacedText.replace(EL_DIR, EL_DIR_REPLACMENT); - return replacedText; - } - - constructor() { - super(); - /** @type {boolean} */ - this.__autoDirOptOut = false; - } - - /** - * @override - * @suppress {invalidCasts} Closure doesn't understand that `this` is an - * HTMLElement - * @return {void} - */ - ready() { - super.ready(); - this.__autoDirOptOut = /** @type {!HTMLElement} */(this).hasAttribute('dir'); - } - - /** - * @override - * @suppress {missingProperties} If it exists on elementBase, it can be - * super'd - * @return {void} - */ - connectedCallback() { - if (elementBase.prototype.connectedCallback) { - super.connectedCallback(); - } - if (this.constructor.__activateDir) { - takeRecords(); - DIR_INSTANCES.push(this); - setRTL(this); - } - } - - /** - * @override - * @suppress {missingProperties} If it exists on elementBase, it can be - * super'd - * @return {void} - */ - disconnectedCallback() { - if (elementBase.prototype.disconnectedCallback) { - super.disconnectedCallback(); - } - if (this.constructor.__activateDir) { - const idx = DIR_INSTANCES.indexOf(this); - if (idx > -1) { - DIR_INSTANCES.splice(idx, 1); - } - } - } - } - - Dir.__activateDir = false; - - return Dir; -});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/disable-upgrade-mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/disable-upgrade-mixin.js deleted file mode 100644 index a97aa9e9..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/disable-upgrade-mixin.js +++ /dev/null
@@ -1,122 +0,0 @@ -/** - * @fileoverview - * @suppress {checkPrototypalTypes} - * @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt The complete set of authors may be found - * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may - * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by - * Google as part of the polymer project is also subject to an additional IP - * rights grant found at http://polymer.github.io/PATENTS.txt - */ -import { ElementMixin } from './element-mixin.js'; - -import { dedupingMixin } from '../utils/mixin.js'; - -const DISABLED_ATTR = 'disable-upgrade'; - -/** - * Element class mixin that allows the element to boot up in a non-enabled - * state when the `disable-upgrade` attribute is present. This mixin is - * designed to be used with element classes like PolymerElement that perform - * initial startup work when they are first connected. When the - * `disable-upgrade` attribute is removed, if the element is connected, it - * boots up and "enables" as it otherwise would; if it is not connected, the - * element boots up when it is next connected. - * - * Using `disable-upgrade` with PolymerElement prevents any data propagation - * to the element, any element DOM from stamping, or any work done in - * connected/disconnctedCallback from occuring, but it does not prevent work - * done in the element constructor. - * - * Note, this mixin must be applied on top of any element class that - * itself implements a `connectedCallback` so that it can control the work - * done in `connectedCallback`. For example, - * - * MyClass = DisableUpgradeMixin(class extends BaseClass {...}); - * - * @mixinFunction - * @polymer - * @appliesMixin ElementMixin - */ -export const DisableUpgradeMixin = dedupingMixin((base) => { - /** - * @constructor - * @implements {Polymer_ElementMixin} - * @extends {HTMLElement} - * @private - */ - const superClass = ElementMixin(base); - - /** - * @polymer - * @mixinClass - * @implements {Polymer_DisableUpgradeMixin} - */ - class DisableUpgradeClass extends superClass { - - /** - * @suppress {missingProperties} go/missingfnprops - */ - static get observedAttributes() { - return super.observedAttributes.concat(DISABLED_ATTR); - } - - /** - * @override - * @param {string} name Attribute name. - * @param {?string} old The previous value for the attribute. - * @param {?string} value The new value for the attribute. - * @param {?string=} namespace The XML namespace for the attribute. - * @return {undefined} - */ - attributeChangedCallback(name, old, value, namespace) { - if (name == DISABLED_ATTR) { - if (!this.__dataEnabled && value == null && this.isConnected) { - super.connectedCallback(); - } - } else { - super.attributeChangedCallback( - name, old, value, /** @type {null|string} */ (namespace)); - } - } - - /* - NOTE: cannot gate on attribute because this is called before - attributes are delivered. Therefore, we stub this out and - call `super._initializeProperties()` manually. - */ - /** @override */ - _initializeProperties() {} - - // prevent user code in connected from running - /** @override */ - connectedCallback() { - if (this.__dataEnabled || !this.hasAttribute(DISABLED_ATTR)) { - super.connectedCallback(); - } - } - - // prevent element from turning on properties - /** @override */ - _enableProperties() { - if (!this.hasAttribute(DISABLED_ATTR)) { - if (!this.__dataEnabled) { - super._initializeProperties(); - } - super._enableProperties(); - } - } - - // only go if "enabled" - /** @override */ - disconnectedCallback() { - if (this.__dataEnabled) { - super.disconnectedCallback(); - } - } - - } - - return DisableUpgradeClass; -});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/element-mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/element-mixin.js deleted file mode 100644 index af291ac..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/element-mixin.js +++ /dev/null
@@ -1,813 +0,0 @@ -/** - * @fileoverview - * @suppress {checkPrototypalTypes} - * @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt The complete set of authors may be found - * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may - * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by - * Google as part of the polymer project is also subject to an additional IP - * rights grant found at http://polymer.github.io/PATENTS.txt - */ -import '../utils/boot.js'; - -import { rootPath, strictTemplatePolicy, allowTemplateFromDomModule, legacyOptimizations, syncInitialRender } from '../utils/settings.js'; -import { dedupingMixin } from '../utils/mixin.js'; -import { stylesFromTemplate, stylesFromModuleImports } from '../utils/style-gather.js'; -import { pathFromUrl, resolveCss, resolveUrl } from '../utils/resolve-url.js'; -import { DomModule } from '../elements/dom-module.js'; -import { PropertyEffects } from './property-effects.js'; -import { PropertiesMixin } from './properties-mixin.js'; -import { wrap } from '../utils/wrap.js'; - -/** - * Current Polymer version in Semver notation. - * @type {string} Semver notation of the current version of Polymer. - */ -export const version = '3.2.0'; - -const builtCSS = window.ShadyCSS && window.ShadyCSS['cssBuild']; - -/** - * Element class mixin that provides the core API for Polymer's meta-programming - * features including template stamping, data-binding, attribute deserialization, - * and property change observation. - * - * Subclassers may provide the following static getters to return metadata - * used to configure Polymer's features for the class: - * - * - `static get is()`: When the template is provided via a `dom-module`, - * users should return the `dom-module` id from a static `is` getter. If - * no template is needed or the template is provided directly via the - * `template` getter, there is no need to define `is` for the element. - * - * - `static get template()`: Users may provide the template directly (as - * opposed to via `dom-module`) by implementing a static `template` getter. - * The getter must return an `HTMLTemplateElement`. - * - * - `static get properties()`: Should return an object describing - * property-related metadata used by Polymer features (key: property name - * value: object containing property metadata). Valid keys in per-property - * metadata include: - * - `type` (String|Number|Object|Array|...): Used by - * `attributeChangedCallback` to determine how string-based attributes - * are deserialized to JavaScript property values. - * - `notify` (boolean): Causes a change in the property to fire a - * non-bubbling event called `<property>-changed`. Elements that have - * enabled two-way binding to the property use this event to observe changes. - * - `readOnly` (boolean): Creates a getter for the property, but no setter. - * To set a read-only property, use the private setter method - * `_setProperty(property, value)`. - * - `observer` (string): Observer method name that will be called when - * the property changes. The arguments of the method are - * `(value, previousValue)`. - * - `computed` (string): String describing method and dependent properties - * for computing the value of this property (e.g. `'computeFoo(bar, zot)'`). - * Computed properties are read-only by default and can only be changed - * via the return value of the computing method. - * - * - `static get observers()`: Array of strings describing multi-property - * observer methods and their dependent properties (e.g. - * `'observeABC(a, b, c)'`). - * - * The base class provides default implementations for the following standard - * custom element lifecycle callbacks; users may override these, but should - * call the super method to ensure - * - `constructor`: Run when the element is created or upgraded - * - `connectedCallback`: Run each time the element is connected to the - * document - * - `disconnectedCallback`: Run each time the element is disconnected from - * the document - * - `attributeChangedCallback`: Run each time an attribute in - * `observedAttributes` is set or removed (note: this element's default - * `observedAttributes` implementation will automatically return an array - * of dash-cased attributes based on `properties`) - * - * @mixinFunction - * @polymer - * @appliesMixin PropertyEffects - * @appliesMixin PropertiesMixin - * @property rootPath {string} Set to the value of `rootPath`, - * which defaults to the main document path - * @property importPath {string} Set to the value of the class's static - * `importPath` property, which defaults to the path of this element's - * `dom-module` (when `is` is used), but can be overridden for other - * import strategies. - * @summary Element class mixin that provides the core API for Polymer's - * meta-programming features. - */ -export const ElementMixin = dedupingMixin(base => { - /** - * @constructor - * @implements {Polymer_PropertyEffects} - * @implements {Polymer_PropertiesMixin} - * @extends {HTMLElement} - * @private - */ - const polymerElementBase = PropertiesMixin(PropertyEffects(base)); - - /** - * Returns a list of properties with default values. - * This list is created as an optimization since it is a subset of - * the list returned from `_properties`. - * This list is used in `_initializeProperties` to set property defaults. - * - * @param {PolymerElementConstructor} constructor Element class - * @return {PolymerElementProperties} Flattened properties for this class - * that have default values - * @private - */ - function propertyDefaults(constructor) { - if (!constructor.hasOwnProperty( - JSCompiler_renameProperty('__propertyDefaults', constructor))) { - constructor.__propertyDefaults = null; - let props = constructor._properties; - for (let p in props) { - let info = props[p]; - if ('value' in info) { - constructor.__propertyDefaults = constructor.__propertyDefaults || {}; - constructor.__propertyDefaults[p] = info; - } - } - } - return constructor.__propertyDefaults; - } - - /** - * Returns a memoized version of the `observers` array. - * @param {PolymerElementConstructor} constructor Element class - * @return {Array} Array containing own observers for the given class - * @protected - */ - function ownObservers(constructor) { - if (!constructor.hasOwnProperty( - JSCompiler_renameProperty('__ownObservers', constructor))) { - constructor.__ownObservers = - constructor.hasOwnProperty( - JSCompiler_renameProperty('observers', constructor)) ? - /** @type {PolymerElementConstructor} */ (constructor).observers : - null; - } - return constructor.__ownObservers; - } - - /** - * Creates effects for a property. - * - * Note, once a property has been set to - * `readOnly`, `computed`, `reflectToAttribute`, or `notify` - * these values may not be changed. For example, a subclass cannot - * alter these settings. However, additional `observers` may be added - * by subclasses. - * - * The info object should contain property metadata as follows: - * - * * `type`: {function} type to which an attribute matching the property - * is deserialized. Note the property is camel-cased from a dash-cased - * attribute. For example, 'foo-bar' attribute is deserialized to a - * property named 'fooBar'. - * - * * `readOnly`: {boolean} creates a readOnly property and - * makes a private setter for the private of the form '_setFoo' for a - * property 'foo', - * - * * `computed`: {string} creates a computed property. A computed property - * is also automatically set to `readOnly: true`. The value is calculated - * by running a method and arguments parsed from the given string. For - * example 'compute(foo)' will compute a given property when the - * 'foo' property changes by executing the 'compute' method. This method - * must return the computed value. - * - * * `reflectToAttribute`: {boolean} If true, the property value is reflected - * to an attribute of the same name. Note, the attribute is dash-cased - * so a property named 'fooBar' is reflected as 'foo-bar'. - * - * * `notify`: {boolean} sends a non-bubbling notification event when - * the property changes. For example, a property named 'foo' sends an - * event named 'foo-changed' with `event.detail` set to the value of - * the property. - * - * * observer: {string} name of a method that runs when the property - * changes. The arguments of the method are (value, previousValue). - * - * Note: Users may want control over modifying property - * effects via subclassing. For example, a user might want to make a - * reflectToAttribute property not do so in a subclass. We've chosen to - * disable this because it leads to additional complication. - * For example, a readOnly effect generates a special setter. If a subclass - * disables the effect, the setter would fail unexpectedly. - * Based on feedback, we may want to try to make effects more malleable - * and/or provide an advanced api for manipulating them. - * - * @param {!PolymerElement} proto Element class prototype to add accessors - * and effects to - * @param {string} name Name of the property. - * @param {Object} info Info object from which to create property effects. - * Supported keys: - * @param {Object} allProps Flattened map of all properties defined in this - * element (including inherited properties) - * @return {void} - * @private - */ - function createPropertyFromConfig(proto, name, info, allProps) { - // computed forces readOnly... - if (info.computed) { - info.readOnly = true; - } - // Note, since all computed properties are readOnly, this prevents - // adding additional computed property effects (which leads to a confusing - // setup where multiple triggers for setting a property) - // While we do have `hasComputedEffect` this is set on the property's - // dependencies rather than itself. - if (info.computed) { - if (proto._hasReadOnlyEffect(name)) { - console.warn(`Cannot redefine computed property '${name}'.`); - } else { - proto._createComputedProperty(name, info.computed, allProps); - } - } - if (info.readOnly && !proto._hasReadOnlyEffect(name)) { - proto._createReadOnlyProperty(name, !info.computed); - } else if (info.readOnly === false && proto._hasReadOnlyEffect(name)) { - console.warn(`Cannot make readOnly property '${name}' non-readOnly.`); - } - if (info.reflectToAttribute && !proto._hasReflectEffect(name)) { - proto._createReflectedProperty(name); - } else if (info.reflectToAttribute === false && proto._hasReflectEffect(name)) { - console.warn(`Cannot make reflected property '${name}' non-reflected.`); - } - if (info.notify && !proto._hasNotifyEffect(name)) { - proto._createNotifyingProperty(name); - } else if (info.notify === false && proto._hasNotifyEffect(name)) { - console.warn(`Cannot make notify property '${name}' non-notify.`); - } - // always add observer - if (info.observer) { - proto._createPropertyObserver(name, info.observer, allProps[info.observer]); - } - // always create the mapping from attribute back to property for deserialization. - proto._addPropertyToAttributeMap(name); - } - - /** - * Process all style elements in the element template. Styles with the - * `include` attribute are processed such that any styles in - * the associated "style modules" are included in the element template. - * @param {PolymerElementConstructor} klass Element class - * @param {!HTMLTemplateElement} template Template to process - * @param {string} is Name of element - * @param {string} baseURI Base URI for element - * @private - */ - function processElementStyles(klass, template, is, baseURI) { - if (!builtCSS) { - const templateStyles = template.content.querySelectorAll('style'); - const stylesWithImports = stylesFromTemplate(template); - // insert styles from <link rel="import" type="css"> at the top of the template - const linkedStyles = stylesFromModuleImports(is); - const firstTemplateChild = template.content.firstElementChild; - for (let idx = 0; idx < linkedStyles.length; idx++) { - let s = linkedStyles[idx]; - s.textContent = klass._processStyleText(s.textContent, baseURI); - template.content.insertBefore(s, firstTemplateChild); - } - // keep track of the last "concrete" style in the template we have encountered - let templateStyleIndex = 0; - // ensure all gathered styles are actually in this template. - for (let i = 0; i < stylesWithImports.length; i++) { - let s = stylesWithImports[i]; - let templateStyle = templateStyles[templateStyleIndex]; - // if the style is not in this template, it's been "included" and - // we put a clone of it in the template before the style that included it - if (templateStyle !== s) { - s = s.cloneNode(true); - templateStyle.parentNode.insertBefore(s, templateStyle); - } else { - templateStyleIndex++; - } - s.textContent = klass._processStyleText(s.textContent, baseURI); - } - } - if (window.ShadyCSS) { - window.ShadyCSS.prepareTemplate(template, is); - } - } - - /** - * Look up template from dom-module for element - * - * @param {string} is Element name to look up - * @return {?HTMLTemplateElement|undefined} Template found in dom module, or - * undefined if not found - * @protected - */ - function getTemplateFromDomModule(is) { - let template = null; - // Under strictTemplatePolicy in 3.x+, dom-module lookup is only allowed - // when opted-in via allowTemplateFromDomModule - if (is && (!strictTemplatePolicy || allowTemplateFromDomModule)) { - template = /** @type {?HTMLTemplateElement} */ ( - DomModule.import(is, 'template')); - // Under strictTemplatePolicy, require any element with an `is` - // specified to have a dom-module - if (strictTemplatePolicy && !template) { - throw new Error(`strictTemplatePolicy: expecting dom-module or null template for ${is}`); - } - } - return template; - } - - /** - * @polymer - * @mixinClass - * @unrestricted - * @implements {Polymer_ElementMixin} - * @extends {polymerElementBase} - */ - class PolymerElement extends polymerElementBase { - - /** - * Current Polymer version in Semver notation. - * @type {string} Semver notation of the current version of Polymer. - */ - static get polymerElementVersion() { - return version; - } - - /** - * Override of PropertiesMixin _finalizeClass to create observers and - * find the template. - * @return {void} - * @protected - * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do - */ - static _finalizeClass() { - super._finalizeClass(); - const observers = ownObservers(this); - if (observers) { - this.createObservers(observers, this._properties); - } - this._prepareTemplate(); - } - - static _prepareTemplate() { - // note: create "working" template that is finalized at instance time - let template = /** @type {PolymerElementConstructor} */ (this).template; - if (template) { - if (typeof template === 'string') { - console.error('template getter must return HTMLTemplateElement'); - template = null; - } else if (!legacyOptimizations) { - template = template.cloneNode(true); - } - } - - this.prototype._template = template; - } - - /** - * Override of PropertiesChanged createProperties to create accessors - * and property effects for all of the properties. - * @param {!Object} props . - * @return {void} - * @protected - */ - static createProperties(props) { - for (let p in props) { - createPropertyFromConfig(this.prototype, p, props[p], props); - } - } - - /** - * Creates observers for the given `observers` array. - * Leverages `PropertyEffects` to create observers. - * @param {Object} observers Array of observer descriptors for - * this class - * @param {Object} dynamicFns Object containing keys for any properties - * that are functions and should trigger the effect when the function - * reference is changed - * @return {void} - * @protected - */ - static createObservers(observers, dynamicFns) { - const proto = this.prototype; - for (let i=0; i < observers.length; i++) { - proto._createMethodObserver(observers[i], dynamicFns); - } - } - - /** - * Returns the template that will be stamped into this element's shadow root. - * - * If a `static get is()` getter is defined, the default implementation - * will return the first `<template>` in a `dom-module` whose `id` - * matches this element's `is`. - * - * Users may override this getter to return an arbitrary template - * (in which case the `is` getter is unnecessary). The template returned - * must be an `HTMLTemplateElement`. - * - * Note that when subclassing, if the super class overrode the default - * implementation and the subclass would like to provide an alternate - * template via a `dom-module`, it should override this getter and - * return `DomModule.import(this.is, 'template')`. - * - * If a subclass would like to modify the super class template, it should - * clone it rather than modify it in place. If the getter does expensive - * work such as cloning/modifying a template, it should memoize the - * template for maximum performance: - * - * let memoizedTemplate; - * class MySubClass extends MySuperClass { - * static get template() { - * if (!memoizedTemplate) { - * memoizedTemplate = super.template.cloneNode(true); - * let subContent = document.createElement('div'); - * subContent.textContent = 'This came from MySubClass'; - * memoizedTemplate.content.appendChild(subContent); - * } - * return memoizedTemplate; - * } - * } - * - * @return {!HTMLTemplateElement|string} Template to be stamped - */ - static get template() { - // Explanation of template-related properties: - // - constructor.template (this getter): the template for the class. - // This can come from the prototype (for legacy elements), from a - // dom-module, or from the super class's template (or can be overridden - // altogether by the user) - // - constructor._template: memoized version of constructor.template - // - prototype._template: working template for the element, which will be - // parsed and modified in place. It is a cloned version of - // constructor.template, saved in _finalizeClass(). Note that before - // this getter is called, for legacy elements this could be from a - // _template field on the info object passed to Polymer(), a behavior, - // or set in registered(); once the static getter runs, a clone of it - // will overwrite it on the prototype as the working template. - if (!this.hasOwnProperty(JSCompiler_renameProperty('_template', this))) { - this._template = - // If user has put template on prototype (e.g. in legacy via registered - // callback or info object), prefer that first - this.prototype.hasOwnProperty(JSCompiler_renameProperty('_template', this.prototype)) ? - this.prototype._template : - // Look in dom-module associated with this element's is - (getTemplateFromDomModule(/** @type {PolymerElementConstructor}*/ (this).is) || - // Next look for superclass template (call the super impl this - // way so that `this` points to the superclass) - Object.getPrototypeOf(/** @type {PolymerElementConstructor}*/ (this).prototype).constructor.template); - } - return this._template; - } - - /** - * Set the template. - * - * @param {!HTMLTemplateElement|string} value Template to set. - */ - static set template(value) { - this._template = value; - } - - /** - * Path matching the url from which the element was imported. - * - * This path is used to resolve url's in template style cssText. - * The `importPath` property is also set on element instances and can be - * used to create bindings relative to the import path. - * - * For elements defined in ES modules, users should implement - * `static get importMeta() { return import.meta; }`, and the default - * implementation of `importPath` will return `import.meta.url`'s path. - * For elements defined in HTML imports, this getter will return the path - * to the document containing a `dom-module` element matching this - * element's static `is` property. - * - * Note, this path should contain a trailing `/`. - * - * @return {string} The import path for this element class - * @suppress {missingProperties} - */ - static get importPath() { - if (!this.hasOwnProperty(JSCompiler_renameProperty('_importPath', this))) { - const meta = this.importMeta; - if (meta) { - this._importPath = pathFromUrl(meta.url); - } else { - const module = DomModule.import(/** @type {PolymerElementConstructor} */ (this).is); - this._importPath = (module && module.assetpath) || - Object.getPrototypeOf(/** @type {PolymerElementConstructor}*/ (this).prototype).constructor.importPath; - } - } - return this._importPath; - } - - constructor() { - super(); - /** @type {HTMLTemplateElement} */ - this._template; - /** @type {string} */ - this._importPath; - /** @type {string} */ - this.rootPath; - /** @type {string} */ - this.importPath; - /** @type {StampedTemplate | HTMLElement | ShadowRoot} */ - this.root; - /** @type {!Object<string, !Element>} */ - this.$; - } - - /** - * Overrides the default `PropertyAccessors` to ensure class - * metaprogramming related to property accessors and effects has - * completed (calls `finalize`). - * - * It also initializes any property defaults provided via `value` in - * `properties` metadata. - * - * @return {void} - * @override - * @suppress {invalidCasts,missingProperties} go/missingfnprops - */ - _initializeProperties() { - this.constructor.finalize(); - // note: finalize template when we have access to `localName` to - // avoid dependence on `is` for polyfilling styling. - this.constructor._finalizeTemplate(/** @type {!HTMLElement} */(this).localName); - super._initializeProperties(); - // set path defaults - this.rootPath = rootPath; - this.importPath = this.constructor.importPath; - // apply property defaults... - let p$ = propertyDefaults(this.constructor); - if (!p$) { - return; - } - for (let p in p$) { - let info = p$[p]; - // Don't set default value if there is already an own property, which - // happens when a `properties` property with default but no effects had - // a property set (e.g. bound) by its host before upgrade - if (!this.hasOwnProperty(p)) { - let value = typeof info.value == 'function' ? - info.value.call(this) : - info.value; - // Set via `_setProperty` if there is an accessor, to enable - // initializing readOnly property defaults - if (this._hasAccessor(p)) { - this._setPendingProperty(p, value, true); - } else { - this[p] = value; - } - } - } - } - - /** - * Gather style text for a style element in the template. - * - * @param {string} cssText Text containing styling to process - * @param {string} baseURI Base URI to rebase CSS paths against - * @return {string} The processed CSS text - * @protected - */ - static _processStyleText(cssText, baseURI) { - return resolveCss(cssText, baseURI); - } - - /** - * Configures an element `proto` to function with a given `template`. - * The element name `is` and extends `ext` must be specified for ShadyCSS - * style scoping. - * - * @param {string} is Tag name (or type extension name) for this element - * @return {void} - * @protected - */ - static _finalizeTemplate(is) { - /** @const {HTMLTemplateElement} */ - const template = this.prototype._template; - if (template && !template.__polymerFinalized) { - template.__polymerFinalized = true; - const importPath = this.importPath; - const baseURI = importPath ? resolveUrl(importPath) : ''; - // e.g. support `include="module-name"`, and ShadyCSS - processElementStyles(this, template, is, baseURI); - this.prototype._bindTemplate(template); - } - } - - /** - * Provides a default implementation of the standard Custom Elements - * `connectedCallback`. - * - * The default implementation enables the property effects system and - * flushes any pending properties, and updates shimmed CSS properties - * when using the ShadyCSS scoping/custom properties polyfill. - * - * @override - * @suppress {missingProperties, invalidCasts} Super may or may not - * implement the callback - * @return {void} - */ - connectedCallback() { - if (window.ShadyCSS && this._template) { - window.ShadyCSS.styleElement(/** @type {!HTMLElement} */(this)); - } - super.connectedCallback(); - } - - /** - * Stamps the element template. - * - * @return {void} - * @override - */ - ready() { - if (this._template) { - this.root = this._stampTemplate(this._template); - this.$ = this.root.$; - } - super.ready(); - } - - /** - * Implements `PropertyEffects`'s `_readyClients` call. Attaches - * element dom by calling `_attachDom` with the dom stamped from the - * element's template via `_stampTemplate`. Note that this allows - * client dom to be attached to the element prior to any observers - * running. - * - * @return {void} - * @override - */ - _readyClients() { - if (this._template) { - this.root = this._attachDom(/** @type {StampedTemplate} */(this.root)); - } - // The super._readyClients here sets the clients initialized flag. - // We must wait to do this until after client dom is created/attached - // so that this flag can be checked to prevent notifications fired - // during this process from being handled before clients are ready. - super._readyClients(); - } - - - /** - * Attaches an element's stamped dom to itself. By default, - * this method creates a `shadowRoot` and adds the dom to it. - * However, this method may be overridden to allow an element - * to put its dom in another location. - * - * @override - * @throws {Error} - * @suppress {missingReturn} - * @param {StampedTemplate} dom to attach to the element. - * @return {ShadowRoot} node to which the dom has been attached. - */ - _attachDom(dom) { - const n = wrap(this); - if (n.attachShadow) { - if (dom) { - if (!n.shadowRoot) { - n.attachShadow({mode: 'open'}); - } - n.shadowRoot.appendChild(dom); - if (syncInitialRender && window.ShadyDOM) { - ShadyDOM.flushInitial(n.shadowRoot); - } - return n.shadowRoot; - } - return null; - } else { - throw new Error('ShadowDOM not available. ' + - // TODO(sorvell): move to compile-time conditional when supported - 'PolymerElement can create dom as children instead of in ' + - 'ShadowDOM by setting `this.root = this;\` before \`ready\`.'); - } - } - - /** - * When using the ShadyCSS scoping and custom property shim, causes all - * shimmed styles in this element (and its subtree) to be updated - * based on current custom property values. - * - * The optional parameter overrides inline custom property styles with an - * object of properties where the keys are CSS properties, and the values - * are strings. - * - * Example: `this.updateStyles({'--color': 'blue'})` - * - * These properties are retained unless a value of `null` is set. - * - * Note: This function does not support updating CSS mixins. - * You can not dynamically change the value of an `@apply`. - * - * @override - * @param {Object=} properties Bag of custom property key/values to - * apply to this element. - * @return {void} - * @suppress {invalidCasts} - */ - updateStyles(properties) { - if (window.ShadyCSS) { - window.ShadyCSS.styleSubtree(/** @type {!HTMLElement} */(this), properties); - } - } - - /** - * Rewrites a given URL relative to a base URL. The base URL defaults to - * the original location of the document containing the `dom-module` for - * this element. This method will return the same URL before and after - * bundling. - * - * Note that this function performs no resolution for URLs that start - * with `/` (absolute URLs) or `#` (hash identifiers). For general purpose - * URL resolution, use `window.URL`. - * - * @override - * @param {string} url URL to resolve. - * @param {string=} base Optional base URL to resolve against, defaults - * to the element's `importPath` - * @return {string} Rewritten URL relative to base - */ - resolveUrl(url, base) { - if (!base && this.importPath) { - base = resolveUrl(this.importPath); - } - return resolveUrl(url, base); - } - - /** - * Overrides `PropertyEffects` to add map of dynamic functions on - * template info, for consumption by `PropertyEffects` template binding - * code. This map determines which method templates should have accessors - * created for them. - * - * @param {!HTMLTemplateElement} template Template - * @param {!TemplateInfo} templateInfo Template metadata for current template - * @param {!NodeInfo} nodeInfo Node metadata for current template. - * @return {boolean} . - * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do - */ - static _parseTemplateContent(template, templateInfo, nodeInfo) { - templateInfo.dynamicFns = templateInfo.dynamicFns || this._properties; - return super._parseTemplateContent(template, templateInfo, nodeInfo); - } - - /** - * Overrides `PropertyEffects` to warn on use of undeclared properties in - * template. - * - * @param {Object} templateInfo Template metadata to add effect to - * @param {string} prop Property that should trigger the effect - * @param {Object=} effect Effect metadata object - * @return {void} - * @protected - * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do - */ - static _addTemplatePropertyEffect(templateInfo, prop, effect) { - // Warn if properties are used in template without being declared. - // Properties must be listed in `properties` to be included in - // `observedAttributes` since CE V1 reads that at registration time, and - // since we want to keep template parsing lazy, we can't automatically - // add undeclared properties used in templates to `observedAttributes`. - // The warning is only enabled in `legacyOptimizations` mode, since - // we don't want to spam existing users who might have adopted the - // shorthand when attribute deserialization is not important. - if (legacyOptimizations && !(prop in this._properties)) { - console.warn(`Property '${prop}' used in template but not declared in 'properties'; ` + - `attribute will not be observed.`); - } - return super._addTemplatePropertyEffect(templateInfo, prop, effect); - } - - } - - return PolymerElement; -}); - -/** - * When using the ShadyCSS scoping and custom property shim, causes all - * shimmed `styles` (via `custom-style`) in the document (and its subtree) - * to be updated based on current custom property values. - * - * The optional parameter overrides inline custom property styles with an - * object of properties where the keys are CSS properties, and the values - * are strings. - * - * Example: `updateStyles({'--color': 'blue'})` - * - * These properties are retained unless a value of `null` is set. - * - * @param {Object=} props Bag of custom property key/values to - * apply to the document. - * @return {void} - */ -export const updateStyles = function(props) { - if (window.ShadyCSS) { - window.ShadyCSS.styleDocument(props); - } -};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/gesture-event-listeners.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/gesture-event-listeners.js deleted file mode 100644 index 04ad6ef..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/gesture-event-listeners.js +++ /dev/null
@@ -1,75 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import '../utils/boot.js'; - -import { dedupingMixin } from '../utils/mixin.js'; -import { addListener, removeListener } from '../utils/gestures.js'; - -/** - * Element class mixin that provides API for adding Polymer's cross-platform - * gesture events to nodes. - * - * The API is designed to be compatible with override points implemented - * in `TemplateStamp` such that declarative event listeners in - * templates will support gesture events when this mixin is applied along with - * `TemplateStamp`. - * - * @mixinFunction - * @polymer - * @summary Element class mixin that provides API for adding Polymer's - * cross-platform - * gesture events to nodes - */ -export const GestureEventListeners = dedupingMixin( - /** - * @template T - * @param {function(new:T)} superClass Class to apply mixin to. - * @return {function(new:T)} superClass with mixin applied. - */ - (superClass) => { - /** - * @polymer - * @mixinClass - * @implements {Polymer_GestureEventListeners} - */ - class GestureEventListeners extends superClass { - /** - * Add the event listener to the node if it is a gestures event. - * - * @param {!EventTarget} node Node to add event listener to - * @param {string} eventName Name of event - * @param {function(!Event):void} handler Listener function to add - * @return {void} - * @override - */ - _addEventListenerToNode(node, eventName, handler) { - if (!addListener(node, eventName, handler)) { - super._addEventListenerToNode(node, eventName, handler); - } - } - - /** - * Remove the event listener to the node if it is a gestures event. - * - * @param {!EventTarget} node Node to remove event listener from - * @param {string} eventName Name of event - * @param {function(!Event):void} handler Listener function to remove - * @return {void} - * @override - */ - _removeEventListenerFromNode(node, eventName, handler) { - if (!removeListener(node, eventName, handler)) { - super._removeEventListenerFromNode(node, eventName, handler); - } - } - } - - return GestureEventListeners; - });
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/mutable-data.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/mutable-data.js deleted file mode 100644 index 3c937b94..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/mutable-data.js +++ /dev/null
@@ -1,194 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import { dedupingMixin } from '../utils/mixin.js'; - -// Common implementation for mixin & behavior -function mutablePropertyChange(inst, property, value, old, mutableData) { - let isObject; - if (mutableData) { - isObject = (typeof value === 'object' && value !== null); - // Pull `old` for Objects from temp cache, but treat `null` as a primitive - if (isObject) { - old = inst.__dataTemp[property]; - } - } - // Strict equality check, but return false for NaN===NaN - let shouldChange = (old !== value && (old === old || value === value)); - // Objects are stored in temporary cache (cleared at end of - // turn), which is used for dirty-checking - if (isObject && shouldChange) { - inst.__dataTemp[property] = value; - } - return shouldChange; -} - -/** - * Element class mixin to skip strict dirty-checking for objects and arrays - * (always consider them to be "dirty"), for use on elements utilizing - * `PropertyEffects` - * - * By default, `PropertyEffects` performs strict dirty checking on - * objects, which means that any deep modifications to an object or array will - * not be propagated unless "immutable" data patterns are used (i.e. all object - * references from the root to the mutation were changed). - * - * Polymer also provides a proprietary data mutation and path notification API - * (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient - * mutation and notification of deep changes in an object graph to all elements - * bound to the same object graph. - * - * In cases where neither immutable patterns nor the data mutation API can be - * used, applying this mixin will cause Polymer to skip dirty checking for - * objects and arrays (always consider them to be "dirty"). This allows a - * user to make a deep modification to a bound object graph, and then either - * simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath` - * (e.g. `this.notifyPath('items')`) to update the tree. Note that all - * elements that wish to be updated based on deep mutations must apply this - * mixin or otherwise skip strict dirty checking for objects/arrays. - * Specifically, any elements in the binding tree between the source of a - * mutation and the consumption of it must apply this mixin or enable the - * `OptionalMutableData` mixin. - * - * In order to make the dirty check strategy configurable, see - * `OptionalMutableData`. - * - * Note, the performance characteristics of propagating large object graphs - * will be worse as opposed to using strict dirty checking with immutable - * patterns or Polymer's path notification API. - * - * @mixinFunction - * @polymer - * @summary Element class mixin to skip strict dirty-checking for objects - * and arrays - */ -export const MutableData = dedupingMixin(superClass => { - - /** - * @polymer - * @mixinClass - * @implements {Polymer_MutableData} - */ - class MutableData extends superClass { - /** - * Overrides `PropertyEffects` to provide option for skipping - * strict equality checking for Objects and Arrays. - * - * This method pulls the value to dirty check against from the `__dataTemp` - * cache (rather than the normal `__data` cache) for Objects. Since the temp - * cache is cleared at the end of a turn, this implementation allows - * side-effects of deep object changes to be processed by re-setting the - * same object (using the temp cache as an in-turn backstop to prevent - * cycles due to 2-way notification). - * - * @param {string} property Property name - * @param {*} value New property value - * @param {*} old Previous property value - * @return {boolean} Whether the property should be considered a change - * @protected - */ - _shouldPropertyChange(property, value, old) { - return mutablePropertyChange(this, property, value, old, true); - } - - } - - return MutableData; - -}); - -/** - * Element class mixin to add the optional ability to skip strict - * dirty-checking for objects and arrays (always consider them to be - * "dirty") by setting a `mutable-data` attribute on an element instance. - * - * By default, `PropertyEffects` performs strict dirty checking on - * objects, which means that any deep modifications to an object or array will - * not be propagated unless "immutable" data patterns are used (i.e. all object - * references from the root to the mutation were changed). - * - * Polymer also provides a proprietary data mutation and path notification API - * (e.g. `notifyPath`, `set`, and array mutation API's) that allow efficient - * mutation and notification of deep changes in an object graph to all elements - * bound to the same object graph. - * - * In cases where neither immutable patterns nor the data mutation API can be - * used, applying this mixin will allow Polymer to skip dirty checking for - * objects and arrays (always consider them to be "dirty"). This allows a - * user to make a deep modification to a bound object graph, and then either - * simply re-set the object (e.g. `this.items = this.items`) or call `notifyPath` - * (e.g. `this.notifyPath('items')`) to update the tree. Note that all - * elements that wish to be updated based on deep mutations must apply this - * mixin or otherwise skip strict dirty checking for objects/arrays. - * Specifically, any elements in the binding tree between the source of a - * mutation and the consumption of it must enable this mixin or apply the - * `MutableData` mixin. - * - * While this mixin adds the ability to forgo Object/Array dirty checking, - * the `mutableData` flag defaults to false and must be set on the instance. - * - * Note, the performance characteristics of propagating large object graphs - * will be worse by relying on `mutableData: true` as opposed to using - * strict dirty checking with immutable patterns or Polymer's path notification - * API. - * - * @mixinFunction - * @polymer - * @summary Element class mixin to optionally skip strict dirty-checking - * for objects and arrays - */ -export const OptionalMutableData = dedupingMixin(superClass => { - - /** - * @mixinClass - * @polymer - * @implements {Polymer_OptionalMutableData} - */ - class OptionalMutableData extends superClass { - - static get properties() { - return { - /** - * Instance-level flag for configuring the dirty-checking strategy - * for this element. When true, Objects and Arrays will skip dirty - * checking, otherwise strict equality checking will be used. - */ - mutableData: Boolean - }; - } - - /** - * Overrides `PropertyEffects` to provide option for skipping - * strict equality checking for Objects and Arrays. - * - * When `this.mutableData` is true on this instance, this method - * pulls the value to dirty check against from the `__dataTemp` cache - * (rather than the normal `__data` cache) for Objects. Since the temp - * cache is cleared at the end of a turn, this implementation allows - * side-effects of deep object changes to be processed by re-setting the - * same object (using the temp cache as an in-turn backstop to prevent - * cycles due to 2-way notification). - * - * @param {string} property Property name - * @param {*} value New property value - * @param {*} old Previous property value - * @return {boolean} Whether the property should be considered a change - * @protected - */ - _shouldPropertyChange(property, value, old) { - return mutablePropertyChange(this, property, value, old, this.mutableData); - } - } - - return OptionalMutableData; - -}); - -// Export for use by legacy behavior -MutableData._mutablePropertyChange = mutablePropertyChange;
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-changed.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-changed.js deleted file mode 100644 index 78ec739..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-changed.js +++ /dev/null
@@ -1,555 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import '../utils/boot.js'; - -import { dedupingMixin } from '../utils/mixin.js'; -import { microTask } from '../utils/async.js'; -import { wrap } from '../utils/wrap.js'; - -/** @const {!AsyncInterface} */ -const microtask = microTask; - -/** - * Element class mixin that provides basic meta-programming for creating one - * or more property accessors (getter/setter pair) that enqueue an async - * (batched) `_propertiesChanged` callback. - * - * For basic usage of this mixin, call `MyClass.createProperties(props)` - * once at class definition time to create property accessors for properties - * named in props, implement `_propertiesChanged` to react as desired to - * property changes, and implement `static get observedAttributes()` and - * include lowercase versions of any property names that should be set from - * attributes. Last, call `this._enableProperties()` in the element's - * `connectedCallback` to enable the accessors. - * - * @mixinFunction - * @polymer - * @summary Element class mixin for reacting to property changes from - * generated property accessors. - */ -export const PropertiesChanged = dedupingMixin( - /** - * @template T - * @param {function(new:T)} superClass Class to apply mixin to. - * @return {function(new:T)} superClass with mixin applied. - */ - (superClass) => { - - /** - * @polymer - * @mixinClass - * @implements {Polymer_PropertiesChanged} - * @unrestricted - */ - class PropertiesChanged extends superClass { - - /** - * Creates property accessors for the given property names. - * @param {!Object} props Object whose keys are names of accessors. - * @return {void} - * @protected - */ - static createProperties(props) { - const proto = this.prototype; - for (let prop in props) { - // don't stomp an existing accessor - if (!(prop in proto)) { - proto._createPropertyAccessor(prop); - } - } - } - - /** - * Returns an attribute name that corresponds to the given property. - * The attribute name is the lowercased property name. Override to - * customize this mapping. - * @param {string} property Property to convert - * @return {string} Attribute name corresponding to the given property. - * - * @protected - */ - static attributeNameForProperty(property) { - return property.toLowerCase(); - } - - /** - * Override point to provide a type to which to deserialize a value to - * a given property. - * @param {string} name Name of property - * - * @protected - */ - static typeForProperty(name) { } //eslint-disable-line no-unused-vars - - /** - * Creates a setter/getter pair for the named property with its own - * local storage. The getter returns the value in the local storage, - * and the setter calls `_setProperty`, which updates the local storage - * for the property and enqueues a `_propertiesChanged` callback. - * - * This method may be called on a prototype or an instance. Calling - * this method may overwrite a property value that already exists on - * the prototype/instance by creating the accessor. - * - * @param {string} property Name of the property - * @param {boolean=} readOnly When true, no setter is created; the - * protected `_setProperty` function must be used to set the property - * @return {void} - * @protected - * @override - */ - _createPropertyAccessor(property, readOnly) { - this._addPropertyToAttributeMap(property); - if (!this.hasOwnProperty('__dataHasAccessor')) { - this.__dataHasAccessor = Object.assign({}, this.__dataHasAccessor); - } - if (!this.__dataHasAccessor[property]) { - this.__dataHasAccessor[property] = true; - this._definePropertyAccessor(property, readOnly); - } - } - - /** - * Adds the given `property` to a map matching attribute names - * to property names, using `attributeNameForProperty`. This map is - * used when deserializing attribute values to properties. - * - * @param {string} property Name of the property - * @override - */ - _addPropertyToAttributeMap(property) { - if (!this.hasOwnProperty('__dataAttributes')) { - this.__dataAttributes = Object.assign({}, this.__dataAttributes); - } - if (!this.__dataAttributes[property]) { - const attr = this.constructor.attributeNameForProperty(property); - this.__dataAttributes[attr] = property; - } - } - - /** - * Defines a property accessor for the given property. - * @param {string} property Name of the property - * @param {boolean=} readOnly When true, no setter is created - * @return {void} - * @override - */ - _definePropertyAccessor(property, readOnly) { - Object.defineProperty(this, property, { - /* eslint-disable valid-jsdoc */ - /** @this {PropertiesChanged} */ - get() { - return this._getProperty(property); - }, - /** @this {PropertiesChanged} */ - set: readOnly ? function () {} : function (value) { - this._setProperty(property, value); - } - /* eslint-enable */ - }); - } - - constructor() { - super(); - /** @protected {boolean} */ - this.__dataEnabled = false; - this.__dataReady = false; - this.__dataInvalid = false; - this.__data = {}; - this.__dataPending = null; - this.__dataOld = null; - this.__dataInstanceProps = null; - this.__serializing = false; - this._initializeProperties(); - } - - /** - * Lifecycle callback called when properties are enabled via - * `_enableProperties`. - * - * Users may override this function to implement behavior that is - * dependent on the element having its property data initialized, e.g. - * from defaults (initialized from `constructor`, `_initializeProperties`), - * `attributeChangedCallback`, or values propagated from host e.g. via - * bindings. `super.ready()` must be called to ensure the data system - * becomes enabled. - * - * @return {void} - * @public - * @override - */ - ready() { - this.__dataReady = true; - this._flushProperties(); - } - - /** - * Initializes the local storage for property accessors. - * - * Provided as an override point for performing any setup work prior - * to initializing the property accessor system. - * - * @return {void} - * @protected - * @override - */ - _initializeProperties() { - // Capture instance properties; these will be set into accessors - // during first flush. Don't set them here, since we want - // these to overwrite defaults/constructor assignments - for (let p in this.__dataHasAccessor) { - if (this.hasOwnProperty(p)) { - this.__dataInstanceProps = this.__dataInstanceProps || {}; - this.__dataInstanceProps[p] = this[p]; - delete this[p]; - } - } - } - - /** - * Called at ready time with bag of instance properties that overwrote - * accessors when the element upgraded. - * - * The default implementation sets these properties back into the - * setter at ready time. This method is provided as an override - * point for customizing or providing more efficient initialization. - * - * @param {Object} props Bag of property values that were overwritten - * when creating property accessors. - * @return {void} - * @protected - * @override - */ - _initializeInstanceProperties(props) { - Object.assign(this, props); - } - - /** - * Updates the local storage for a property (via `_setPendingProperty`) - * and enqueues a `_proeprtiesChanged` callback. - * - * @param {string} property Name of the property - * @param {*} value Value to set - * @return {void} - * @protected - * @override - */ - _setProperty(property, value) { - if (this._setPendingProperty(property, value)) { - this._invalidateProperties(); - } - } - - /** - * Returns the value for the given property. - * @param {string} property Name of property - * @return {*} Value for the given property - * @protected - * @override - */ - _getProperty(property) { - return this.__data[property]; - } - - /* eslint-disable no-unused-vars */ - /** - * Updates the local storage for a property, records the previous value, - * and adds it to the set of "pending changes" that will be passed to the - * `_propertiesChanged` callback. This method does not enqueue the - * `_propertiesChanged` callback. - * - * @param {string} property Name of the property - * @param {*} value Value to set - * @param {boolean=} ext Not used here; affordance for closure - * @return {boolean} Returns true if the property changed - * @protected - * @override - */ - _setPendingProperty(property, value, ext) { - let old = this.__data[property]; - let changed = this._shouldPropertyChange(property, value, old); - if (changed) { - if (!this.__dataPending) { - this.__dataPending = {}; - this.__dataOld = {}; - } - // Ensure old is captured from the last turn - if (this.__dataOld && !(property in this.__dataOld)) { - this.__dataOld[property] = old; - } - this.__data[property] = value; - this.__dataPending[property] = value; - } - return changed; - } - /* eslint-enable */ - - /** - * Marks the properties as invalid, and enqueues an async - * `_propertiesChanged` callback. - * - * @return {void} - * @protected - * @override - */ - _invalidateProperties() { - if (!this.__dataInvalid && this.__dataReady) { - this.__dataInvalid = true; - microtask.run(() => { - if (this.__dataInvalid) { - this.__dataInvalid = false; - this._flushProperties(); - } - }); - } - } - - /** - * Call to enable property accessor processing. Before this method is - * called accessor values will be set but side effects are - * queued. When called, any pending side effects occur immediately. - * For elements, generally `connectedCallback` is a normal spot to do so. - * It is safe to call this method multiple times as it only turns on - * property accessors once. - * - * @return {void} - * @protected - * @override - */ - _enableProperties() { - if (!this.__dataEnabled) { - this.__dataEnabled = true; - if (this.__dataInstanceProps) { - this._initializeInstanceProperties(this.__dataInstanceProps); - this.__dataInstanceProps = null; - } - this.ready(); - } - } - - /** - * Calls the `_propertiesChanged` callback with the current set of - * pending changes (and old values recorded when pending changes were - * set), and resets the pending set of changes. Generally, this method - * should not be called in user code. - * - * @return {void} - * @protected - * @override - */ - _flushProperties() { - const props = this.__data; - const changedProps = this.__dataPending; - const old = this.__dataOld; - if (this._shouldPropertiesChange(props, changedProps, old)) { - this.__dataPending = null; - this.__dataOld = null; - this._propertiesChanged(props, changedProps, old); - } - } - - /** - * Called in `_flushProperties` to determine if `_propertiesChanged` - * should be called. The default implementation returns true if - * properties are pending. Override to customize when - * `_propertiesChanged` is called. - * @param {!Object} currentProps Bag of all current accessor values - * @param {?Object} changedProps Bag of properties changed since the last - * call to `_propertiesChanged` - * @param {?Object} oldProps Bag of previous values for each property - * in `changedProps` - * @return {boolean} true if changedProps is truthy - * @override - */ - _shouldPropertiesChange(currentProps, changedProps, oldProps) { // eslint-disable-line no-unused-vars - return Boolean(changedProps); - } - - /** - * Callback called when any properties with accessors created via - * `_createPropertyAccessor` have been set. - * - * @param {!Object} currentProps Bag of all current accessor values - * @param {?Object} changedProps Bag of properties changed since the last - * call to `_propertiesChanged` - * @param {?Object} oldProps Bag of previous values for each property - * in `changedProps` - * @return {void} - * @protected - * @override - */ - _propertiesChanged(currentProps, changedProps, oldProps) { // eslint-disable-line no-unused-vars - } - - /** - * Method called to determine whether a property value should be - * considered as a change and cause the `_propertiesChanged` callback - * to be enqueued. - * - * The default implementation returns `true` if a strict equality - * check fails. The method always returns false for `NaN`. - * - * Override this method to e.g. provide stricter checking for - * Objects/Arrays when using immutable patterns. - * - * @param {string} property Property name - * @param {*} value New property value - * @param {*} old Previous property value - * @return {boolean} Whether the property should be considered a change - * and enqueue a `_proeprtiesChanged` callback - * @protected - * @override - */ - _shouldPropertyChange(property, value, old) { - return ( - // Strict equality check - (old !== value && - // This ensures (old==NaN, value==NaN) always returns false - (old === old || value === value)) - ); - } - - /** - * Implements native Custom Elements `attributeChangedCallback` to - * set an attribute value to a property via `_attributeToProperty`. - * - * @param {string} name Name of attribute that changed - * @param {?string} old Old attribute value - * @param {?string} value New attribute value - * @param {?string=} namespace Attribute namespace. - * @return {void} - * @suppress {missingProperties} Super may or may not implement the callback - * @override - */ - attributeChangedCallback(name, old, value, namespace) { - if (old !== value) { - this._attributeToProperty(name, value); - } - if (super.attributeChangedCallback) { - super.attributeChangedCallback(name, old, value, namespace); - } - } - - /** - * Deserializes an attribute to its associated property. - * - * This method calls the `_deserializeValue` method to convert the string to - * a typed value. - * - * @param {string} attribute Name of attribute to deserialize. - * @param {?string} value of the attribute. - * @param {*=} type type to deserialize to, defaults to the value - * returned from `typeForProperty` - * @return {void} - * @override - */ - _attributeToProperty(attribute, value, type) { - if (!this.__serializing) { - const map = this.__dataAttributes; - const property = map && map[attribute] || attribute; - this[property] = this._deserializeValue(value, type || - this.constructor.typeForProperty(property)); - } - } - - /** - * Serializes a property to its associated attribute. - * - * @suppress {invalidCasts} Closure can't figure out `this` is an element. - * - * @param {string} property Property name to reflect. - * @param {string=} attribute Attribute name to reflect to. - * @param {*=} value Property value to refect. - * @return {void} - * @override - */ - _propertyToAttribute(property, attribute, value) { - this.__serializing = true; - value = (arguments.length < 3) ? this[property] : value; - this._valueToNodeAttribute(/** @type {!HTMLElement} */(this), value, - attribute || this.constructor.attributeNameForProperty(property)); - this.__serializing = false; - } - - /** - * Sets a typed value to an HTML attribute on a node. - * - * This method calls the `_serializeValue` method to convert the typed - * value to a string. If the `_serializeValue` method returns `undefined`, - * the attribute will be removed (this is the default for boolean - * type `false`). - * - * @param {Element} node Element to set attribute to. - * @param {*} value Value to serialize. - * @param {string} attribute Attribute name to serialize to. - * @return {void} - * @override - */ - _valueToNodeAttribute(node, value, attribute) { - const str = this._serializeValue(value); - if (str === undefined) { - node.removeAttribute(attribute); - } else { - if (attribute === 'class' || attribute === 'name' || attribute === 'slot') { - node = /** @type {?Element} */(wrap(node)); - } - node.setAttribute(attribute, str); - } - } - - /** - * Converts a typed JavaScript value to a string. - * - * This method is called when setting JS property values to - * HTML attributes. Users may override this method to provide - * serialization for custom types. - * - * @param {*} value Property value to serialize. - * @return {string | undefined} String serialized from the provided - * property value. - * @override - */ - _serializeValue(value) { - switch (typeof value) { - case 'boolean': - return value ? '' : undefined; - default: - return value != null ? value.toString() : undefined; - } - } - - /** - * Converts a string to a typed JavaScript value. - * - * This method is called when reading HTML attribute values to - * JS properties. Users may override this method to provide - * deserialization for custom `type`s. Types for `Boolean`, `String`, - * and `Number` convert attributes to the expected types. - * - * @param {?string} value Value to deserialize. - * @param {*=} type Type to deserialize the string to. - * @return {*} Typed value deserialized from the provided string. - * @override - */ - _deserializeValue(value, type) { - switch (type) { - case Boolean: - return (value !== null); - case Number: - return Number(value); - default: - return value; - } - } - - } - - return PropertiesChanged; -});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-mixin.js deleted file mode 100644 index 871937b..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-mixin.js +++ /dev/null
@@ -1,233 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import '../utils/boot.js'; - -import { dedupingMixin } from '../utils/mixin.js'; -import { register, incrementInstanceCount } from '../utils/telemetry.js'; -import { PropertiesChanged } from './properties-changed.js'; - -/** - * Creates a copy of `props` with each property normalized such that - * upgraded it is an object with at least a type property { type: Type}. - * - * @param {Object} props Properties to normalize - * @return {Object} Copy of input `props` with normalized properties that - * are in the form {type: Type} - * @private - */ -function normalizeProperties(props) { - const output = {}; - for (let p in props) { - const o = props[p]; - output[p] = (typeof o === 'function') ? {type: o} : o; - } - return output; -} - -/** - * Mixin that provides a minimal starting point to using the PropertiesChanged - * mixin by providing a mechanism to declare properties in a static - * getter (e.g. static get properties() { return { foo: String } }). Changes - * are reported via the `_propertiesChanged` method. - * - * This mixin provides no specific support for rendering. Users are expected - * to create a ShadowRoot and put content into it and update it in whatever - * way makes sense. This can be done in reaction to properties changing by - * implementing `_propertiesChanged`. - * - * @mixinFunction - * @polymer - * @appliesMixin PropertiesChanged - * @summary Mixin that provides a minimal starting point for using - * the PropertiesChanged mixin by providing a declarative `properties` object. - */ -export const PropertiesMixin = dedupingMixin(superClass => { - - /** - * @constructor - * @implements {Polymer_PropertiesChanged} - * @private - */ - const base = PropertiesChanged(superClass); - - /** - * Returns the super class constructor for the given class, if it is an - * instance of the PropertiesMixin. - * - * @param {!PropertiesMixinConstructor} constructor PropertiesMixin constructor - * @return {?PropertiesMixinConstructor} Super class constructor - */ - function superPropertiesClass(constructor) { - const superCtor = Object.getPrototypeOf(constructor); - - // Note, the `PropertiesMixin` class below only refers to the class - // generated by this call to the mixin; the instanceof test only works - // because the mixin is deduped and guaranteed only to apply once, hence - // all constructors in a proto chain will see the same `PropertiesMixin` - return (superCtor.prototype instanceof PropertiesMixin) ? - /** @type {!PropertiesMixinConstructor} */ (superCtor) : null; - } - - /** - * Returns a memoized version of the `properties` object for the - * given class. Properties not in object format are converted to at - * least {type}. - * - * @param {PropertiesMixinConstructor} constructor PropertiesMixin constructor - * @return {Object} Memoized properties object - */ - function ownProperties(constructor) { - if (!constructor.hasOwnProperty(JSCompiler_renameProperty('__ownProperties', constructor))) { - let props = null; - - if (constructor.hasOwnProperty(JSCompiler_renameProperty('properties', constructor))) { - const properties = constructor.properties; - - if (properties) { - props = normalizeProperties(properties); - } - } - - constructor.__ownProperties = props; - } - return constructor.__ownProperties; - } - - /** - * @polymer - * @mixinClass - * @extends {base} - * @implements {Polymer_PropertiesMixin} - * @unrestricted - */ - class PropertiesMixin extends base { - - /** - * Implements standard custom elements getter to observes the attributes - * listed in `properties`. - * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do - */ - static get observedAttributes() { - if (!this.hasOwnProperty('__observedAttributes')) { - register(this.prototype); - const props = this._properties; - this.__observedAttributes = props ? Object.keys(props).map(p => this.attributeNameForProperty(p)) : []; - } - return this.__observedAttributes; - } - - /** - * Finalizes an element definition, including ensuring any super classes - * are also finalized. This includes ensuring property - * accessors exist on the element prototype. This method calls - * `_finalizeClass` to finalize each constructor in the prototype chain. - * @return {void} - */ - static finalize() { - if (!this.hasOwnProperty(JSCompiler_renameProperty('__finalized', this))) { - const superCtor = superPropertiesClass(/** @type {!PropertiesMixinConstructor} */(this)); - if (superCtor) { - superCtor.finalize(); - } - this.__finalized = true; - this._finalizeClass(); - } - } - - /** - * Finalize an element class. This includes ensuring property - * accessors exist on the element prototype. This method is called by - * `finalize` and finalizes the class constructor. - * - * @protected - */ - static _finalizeClass() { - const props = ownProperties(/** @type {!PropertiesMixinConstructor} */(this)); - if (props) { - this.createProperties(props); - } - } - - /** - * Returns a memoized version of all properties, including those inherited - * from super classes. Properties not in object format are converted to - * at least {type}. - * - * @return {Object} Object containing properties for this class - * @protected - */ - static get _properties() { - if (!this.hasOwnProperty( - JSCompiler_renameProperty('__properties', this))) { - const superCtor = superPropertiesClass(/** @type {!PropertiesMixinConstructor} */(this)); - this.__properties = Object.assign({}, - superCtor && superCtor._properties, - ownProperties(/** @type {PropertiesMixinConstructor} */(this))); - } - return this.__properties; - } - - /** - * Overrides `PropertiesChanged` method to return type specified in the - * static `properties` object for the given property. - * @param {string} name Name of property - * @return {*} Type to which to deserialize attribute - * - * @protected - */ - static typeForProperty(name) { - const info = this._properties[name]; - return info && info.type; - } - - /** - * Overrides `PropertiesChanged` method and adds a call to - * `finalize` which lazily configures the element's property accessors. - * @override - * @return {void} - */ - _initializeProperties() { - incrementInstanceCount(); - this.constructor.finalize(); - super._initializeProperties(); - } - - /** - * Called when the element is added to a document. - * Calls `_enableProperties` to turn on property system from - * `PropertiesChanged`. - * @suppress {missingProperties} Super may or may not implement the callback - * @return {void} - * @override - */ - connectedCallback() { - if (super.connectedCallback) { - super.connectedCallback(); - } - this._enableProperties(); - } - - /** - * Called when the element is removed from a document - * @suppress {missingProperties} Super may or may not implement the callback - * @return {void} - * @override - */ - disconnectedCallback() { - if (super.disconnectedCallback) { - super.disconnectedCallback(); - } - } - - } - - return PropertiesMixin; - -});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-accessors.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-accessors.js deleted file mode 100644 index bfa7f33a..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-accessors.js +++ /dev/null
@@ -1,323 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import '../utils/boot.js'; - -import { dedupingMixin } from '../utils/mixin.js'; -import { camelToDashCase, dashToCamelCase } from '../utils/case-map.js'; -import { PropertiesChanged } from './properties-changed.js'; - -// Save map of native properties; this forms a blacklist or properties -// that won't have their values "saved" by `saveAccessorValue`, since -// reading from an HTMLElement accessor from the context of a prototype throws -const nativeProperties = {}; -let proto = HTMLElement.prototype; -while (proto) { - let props = Object.getOwnPropertyNames(proto); - for (let i=0; i<props.length; i++) { - nativeProperties[props[i]] = true; - } - proto = Object.getPrototypeOf(proto); -} - -/** - * Used to save the value of a property that will be overridden with - * an accessor. If the `model` is a prototype, the values will be saved - * in `__dataProto`, and it's up to the user (or downstream mixin) to - * decide how/when to set these values back into the accessors. - * If `model` is already an instance (it has a `__data` property), then - * the value will be set as a pending property, meaning the user should - * call `_invalidateProperties` or `_flushProperties` to take effect - * - * @param {Object} model Prototype or instance - * @param {string} property Name of property - * @return {void} - * @private - */ -function saveAccessorValue(model, property) { - // Don't read/store value for any native properties since they could throw - if (!nativeProperties[property]) { - let value = model[property]; - if (value !== undefined) { - if (model.__data) { - // Adding accessor to instance; update the property - // It is the user's responsibility to call _flushProperties - model._setPendingProperty(property, value); - } else { - // Adding accessor to proto; save proto's value for instance-time use - if (!model.__dataProto) { - model.__dataProto = {}; - } else if (!model.hasOwnProperty(JSCompiler_renameProperty('__dataProto', model))) { - model.__dataProto = Object.create(model.__dataProto); - } - model.__dataProto[property] = value; - } - } - } -} - -/** - * Element class mixin that provides basic meta-programming for creating one - * or more property accessors (getter/setter pair) that enqueue an async - * (batched) `_propertiesChanged` callback. - * - * For basic usage of this mixin: - * - * - Declare attributes to observe via the standard `static get - * observedAttributes()`. Use `dash-case` attribute names to represent - * `camelCase` property names. - * - Implement the `_propertiesChanged` callback on the class. - * - Call `MyClass.createPropertiesForAttributes()` **once** on the class to - * generate property accessors for each observed attribute. This must be - * called before the first instance is created, for example, by calling it - * before calling `customElements.define`. It can also be called lazily from - * the element's `constructor`, as long as it's guarded so that the call is - * only made once, when the first instance is created. - * - Call `this._enableProperties()` in the element's `connectedCallback` to - * enable the accessors. - * - * Any `observedAttributes` will automatically be - * deserialized via `attributeChangedCallback` and set to the associated - * property using `dash-case`-to-`camelCase` convention. - * - * @mixinFunction - * @polymer - * @appliesMixin PropertiesChanged - * @summary Element class mixin for reacting to property changes from - * generated property accessors. - */ -export const PropertyAccessors = dedupingMixin(superClass => { - - /** - * @constructor - * @implements {Polymer_PropertiesChanged} - * @unrestricted - * @private - */ - const base = PropertiesChanged(superClass); - - /** - * @polymer - * @mixinClass - * @implements {Polymer_PropertyAccessors} - * @extends {base} - * @unrestricted - */ - class PropertyAccessors extends base { - - /** - * Generates property accessors for all attributes in the standard - * static `observedAttributes` array. - * - * Attribute names are mapped to property names using the `dash-case` to - * `camelCase` convention - * - * @return {void} - */ - static createPropertiesForAttributes() { - let a$ = this.observedAttributes; - for (let i=0; i < a$.length; i++) { - this.prototype._createPropertyAccessor(dashToCamelCase(a$[i])); - } - } - - /** - * Returns an attribute name that corresponds to the given property. - * By default, converts camel to dash case, e.g. `fooBar` to `foo-bar`. - * @param {string} property Property to convert - * @return {string} Attribute name corresponding to the given property. - * - * @protected - */ - static attributeNameForProperty(property) { - return camelToDashCase(property); - } - - /** - * Overrides PropertiesChanged implementation to initialize values for - * accessors created for values that already existed on the element - * prototype. - * - * @return {void} - * @protected - * @override - */ - _initializeProperties() { - if (this.__dataProto) { - this._initializeProtoProperties(this.__dataProto); - this.__dataProto = null; - } - super._initializeProperties(); - } - - /** - * Called at instance time with bag of properties that were overwritten - * by accessors on the prototype when accessors were created. - * - * The default implementation sets these properties back into the - * setter at instance time. This method is provided as an override - * point for customizing or providing more efficient initialization. - * - * @param {Object} props Bag of property values that were overwritten - * when creating property accessors. - * @return {void} - * @protected - * @override - */ - _initializeProtoProperties(props) { - for (let p in props) { - this._setProperty(p, props[p]); - } - } - - /** - * Ensures the element has the given attribute. If it does not, - * assigns the given value to the attribute. - * - * @suppress {invalidCasts} Closure can't figure out `this` is infact an - * element - * - * @param {string} attribute Name of attribute to ensure is set. - * @param {string} value of the attribute. - * @return {void} - * @override - */ - _ensureAttribute(attribute, value) { - const el = /** @type {!HTMLElement} */(this); - if (!el.hasAttribute(attribute)) { - this._valueToNodeAttribute(el, value, attribute); - } - } - - /** - * Overrides PropertiesChanged implemention to serialize objects as JSON. - * - * @param {*} value Property value to serialize. - * @return {string | undefined} String serialized from the provided property - * value. - * @override - */ - _serializeValue(value) { - /* eslint-disable no-fallthrough */ - switch (typeof value) { - case 'object': - if (value instanceof Date) { - return value.toString(); - } else if (value) { - try { - return JSON.stringify(value); - } catch(x) { - return ''; - } - } - - default: - return super._serializeValue(value); - } - } - - /** - * Converts a string to a typed JavaScript value. - * - * This method is called by Polymer when reading HTML attribute values to - * JS properties. Users may override this method on Polymer element - * prototypes to provide deserialization for custom `type`s. Note, - * the `type` argument is the value of the `type` field provided in the - * `properties` configuration object for a given property, and is - * by convention the constructor for the type to deserialize. - * - * - * @param {?string} value Attribute value to deserialize. - * @param {*=} type Type to deserialize the string to. - * @return {*} Typed value deserialized from the provided string. - * @override - */ - _deserializeValue(value, type) { - /** - * @type {*} - */ - let outValue; - switch (type) { - case Object: - try { - outValue = JSON.parse(/** @type {string} */(value)); - } catch(x) { - // allow non-JSON literals like Strings and Numbers - outValue = value; - } - break; - case Array: - try { - outValue = JSON.parse(/** @type {string} */(value)); - } catch(x) { - outValue = null; - console.warn(`Polymer::Attributes: couldn't decode Array as JSON: ${value}`); - } - break; - case Date: - outValue = isNaN(value) ? String(value) : Number(value); - outValue = new Date(outValue); - break; - default: - outValue = super._deserializeValue(value, type); - break; - } - return outValue; - } - /* eslint-enable no-fallthrough */ - - /** - * Overrides PropertiesChanged implementation to save existing prototype - * property value so that it can be reset. - * @param {string} property Name of the property - * @param {boolean=} readOnly When true, no setter is created - * - * When calling on a prototype, any overwritten values are saved in - * `__dataProto`, and it is up to the subclasser to decide how/when - * to set those properties back into the accessor. When calling on an - * instance, the overwritten value is set via `_setPendingProperty`, - * and the user should call `_invalidateProperties` or `_flushProperties` - * for the values to take effect. - * @protected - * @return {void} - * @override - */ - _definePropertyAccessor(property, readOnly) { - saveAccessorValue(this, property); - super._definePropertyAccessor(property, readOnly); - } - - /** - * Returns true if this library created an accessor for the given property. - * - * @param {string} property Property name - * @return {boolean} True if an accessor was created - * @override - */ - _hasAccessor(property) { - return this.__dataHasAccessor && this.__dataHasAccessor[property]; - } - - /** - * Returns true if the specified property has a pending change. - * - * @param {string} prop Property name - * @return {boolean} True if property has a pending change - * @protected - * @override - */ - _isPropertyPending(prop) { - return Boolean(this.__dataPending && (prop in this.__dataPending)); - } - - } - - return PropertyAccessors; - -});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-effects.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-effects.js deleted file mode 100644 index c92bea3..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-effects.js +++ /dev/null
@@ -1,2856 +0,0 @@ -/** - * @fileoverview - * @suppress {checkPrototypalTypes} - * @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt The complete set of authors may be found - * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may - * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by - * Google as part of the polymer project is also subject to an additional IP - * rights grant found at http://polymer.github.io/PATENTS.txt - */ - -import '../utils/boot.js'; -import { wrap } from '../utils/wrap.js'; -import { dedupingMixin } from '../utils/mixin.js'; -import { root, isAncestor, isDescendant, get, translate, isPath, set, normalize } from '../utils/path.js'; -/* for notify, reflect */ -import { camelToDashCase, dashToCamelCase } from '../utils/case-map.js'; -import { PropertyAccessors } from './property-accessors.js'; -/* for annotated effects */ -import { TemplateStamp } from './template-stamp.js'; -import { sanitizeDOMValue } from '../utils/settings.js'; - -// Monotonically increasing unique ID used for de-duping effects triggered -// from multiple properties in the same turn -let dedupeId = 0; - -/** - * Property effect types; effects are stored on the prototype using these keys - * @enum {string} - */ -const TYPES = { - COMPUTE: '__computeEffects', - REFLECT: '__reflectEffects', - NOTIFY: '__notifyEffects', - PROPAGATE: '__propagateEffects', - OBSERVE: '__observeEffects', - READ_ONLY: '__readOnly' -}; - -/** @const {!RegExp} */ -const capitalAttributeRegex = /[A-Z]/; - -/** - * @typedef {{ - * name: (string | undefined), - * structured: (boolean | undefined), - * wildcard: (boolean | undefined) - * }} - */ -let DataTrigger; //eslint-disable-line no-unused-vars - -/** - * @typedef {{ - * info: ?, - * trigger: (!DataTrigger | undefined), - * fn: (!Function | undefined) - * }} - */ -let DataEffect; //eslint-disable-line no-unused-vars - -/** - * Ensures that the model has an own-property map of effects for the given type. - * The model may be a prototype or an instance. - * - * Property effects are stored as arrays of effects by property in a map, - * by named type on the model. e.g. - * - * __computeEffects: { - * foo: [ ... ], - * bar: [ ... ] - * } - * - * If the model does not yet have an effect map for the type, one is created - * and returned. If it does, but it is not an own property (i.e. the - * prototype had effects), the the map is deeply cloned and the copy is - * set on the model and returned, ready for new effects to be added. - * - * @param {Object} model Prototype or instance - * @param {string} type Property effect type - * @return {Object} The own-property map of effects for the given type - * @private - */ -function ensureOwnEffectMap(model, type) { - let effects = model[type]; - if (!effects) { - effects = model[type] = {}; - } else if (!model.hasOwnProperty(type)) { - effects = model[type] = Object.create(model[type]); - for (let p in effects) { - let protoFx = effects[p]; - let instFx = effects[p] = Array(protoFx.length); - for (let i=0; i<protoFx.length; i++) { - instFx[i] = protoFx[i]; - } - } - } - return effects; -} - -// -- effects ---------------------------------------------- - -/** - * Runs all effects of a given type for the given set of property changes - * on an instance. - * - * @param {!Polymer_PropertyEffects} inst The instance with effects to run - * @param {?Object} effects Object map of property-to-Array of effects - * @param {?Object} props Bag of current property changes - * @param {?Object=} oldProps Bag of previous values for changed properties - * @param {boolean=} hasPaths True with `props` contains one or more paths - * @param {*=} extraArgs Additional metadata to pass to effect function - * @return {boolean} True if an effect ran for this property - * @private - */ -function runEffects(inst, effects, props, oldProps, hasPaths, extraArgs) { - if (effects) { - let ran = false; - let id = dedupeId++; - for (let prop in props) { - if (runEffectsForProperty( - inst, /** @type {!Object} */ (effects), id, prop, props, oldProps, - hasPaths, extraArgs)) { - ran = true; - } - } - return ran; - } - return false; -} - -/** - * Runs a list of effects for a given property. - * - * @param {!Polymer_PropertyEffects} inst The instance with effects to run - * @param {!Object} effects Object map of property-to-Array of effects - * @param {number} dedupeId Counter used for de-duping effects - * @param {string} prop Name of changed property - * @param {*} props Changed properties - * @param {*} oldProps Old properties - * @param {boolean=} hasPaths True with `props` contains one or more paths - * @param {*=} extraArgs Additional metadata to pass to effect function - * @return {boolean} True if an effect ran for this property - * @private - */ -function runEffectsForProperty(inst, effects, dedupeId, prop, props, oldProps, hasPaths, extraArgs) { - let ran = false; - let rootProperty = hasPaths ? root(prop) : prop; - let fxs = effects[rootProperty]; - if (fxs) { - for (let i=0, l=fxs.length, fx; (i<l) && (fx=fxs[i]); i++) { - if ((!fx.info || fx.info.lastRun !== dedupeId) && - (!hasPaths || pathMatchesTrigger(prop, fx.trigger))) { - if (fx.info) { - fx.info.lastRun = dedupeId; - } - fx.fn(inst, prop, props, oldProps, fx.info, hasPaths, extraArgs); - ran = true; - } - } - } - return ran; -} - -/** - * Determines whether a property/path that has changed matches the trigger - * criteria for an effect. A trigger is a descriptor with the following - * structure, which matches the descriptors returned from `parseArg`. - * e.g. for `foo.bar.*`: - * ``` - * trigger: { - * name: 'a.b', - * structured: true, - * wildcard: true - * } - * ``` - * If no trigger is given, the path is deemed to match. - * - * @param {string} path Path or property that changed - * @param {?DataTrigger} trigger Descriptor - * @return {boolean} Whether the path matched the trigger - */ -function pathMatchesTrigger(path, trigger) { - if (trigger) { - let triggerPath = /** @type {string} */ (trigger.name); - return (triggerPath == path) || - !!(trigger.structured && isAncestor(triggerPath, path)) || - !!(trigger.wildcard && isDescendant(triggerPath, path)); - } else { - return true; - } -} - -/** - * Implements the "observer" effect. - * - * Calls the method with `info.methodName` on the instance, passing the - * new and old values. - * - * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on - * @param {string} property Name of property - * @param {Object} props Bag of current property changes - * @param {Object} oldProps Bag of previous values for changed properties - * @param {?} info Effect metadata - * @return {void} - * @private - */ -function runObserverEffect(inst, property, props, oldProps, info) { - let fn = typeof info.method === "string" ? inst[info.method] : info.method; - let changedProp = info.property; - if (fn) { - fn.call(inst, inst.__data[changedProp], oldProps[changedProp]); - } else if (!info.dynamicFn) { - console.warn('observer method `' + info.method + '` not defined'); - } -} - -/** - * Runs "notify" effects for a set of changed properties. - * - * This method differs from the generic `runEffects` method in that it - * will dispatch path notification events in the case that the property - * changed was a path and the root property for that path didn't have a - * "notify" effect. This is to maintain 1.0 behavior that did not require - * `notify: true` to ensure object sub-property notifications were - * sent. - * - * @param {!Polymer_PropertyEffects} inst The instance with effects to run - * @param {Object} notifyProps Bag of properties to notify - * @param {Object} props Bag of current property changes - * @param {Object} oldProps Bag of previous values for changed properties - * @param {boolean} hasPaths True with `props` contains one or more paths - * @return {void} - * @private - */ -function runNotifyEffects(inst, notifyProps, props, oldProps, hasPaths) { - // Notify - let fxs = inst[TYPES.NOTIFY]; - let notified; - let id = dedupeId++; - // Try normal notify effects; if none, fall back to try path notification - for (let prop in notifyProps) { - if (notifyProps[prop]) { - if (fxs && runEffectsForProperty(inst, fxs, id, prop, props, oldProps, hasPaths)) { - notified = true; - } else if (hasPaths && notifyPath(inst, prop, props)) { - notified = true; - } - } - } - // Flush host if we actually notified and host was batching - // And the host has already initialized clients; this prevents - // an issue with a host observing data changes before clients are ready. - let host; - if (notified && (host = inst.__dataHost) && host._invalidateProperties) { - host._invalidateProperties(); - } -} - -/** - * Dispatches {property}-changed events with path information in the detail - * object to indicate a sub-path of the property was changed. - * - * @param {!Polymer_PropertyEffects} inst The element from which to fire the - * event - * @param {string} path The path that was changed - * @param {Object} props Bag of current property changes - * @return {boolean} Returns true if the path was notified - * @private - */ -function notifyPath(inst, path, props) { - let rootProperty = root(path); - if (rootProperty !== path) { - let eventName = camelToDashCase(rootProperty) + '-changed'; - dispatchNotifyEvent(inst, eventName, props[path], path); - return true; - } - return false; -} - -/** - * Dispatches {property}-changed events to indicate a property (or path) - * changed. - * - * @param {!Polymer_PropertyEffects} inst The element from which to fire the - * event - * @param {string} eventName The name of the event to send - * ('{property}-changed') - * @param {*} value The value of the changed property - * @param {string | null | undefined} path If a sub-path of this property - * changed, the path that changed (optional). - * @return {void} - * @private - * @suppress {invalidCasts} - */ -function dispatchNotifyEvent(inst, eventName, value, path) { - let detail = { - value: value, - queueProperty: true - }; - if (path) { - detail.path = path; - } - wrap(/** @type {!HTMLElement} */(inst)).dispatchEvent(new CustomEvent(eventName, { detail })); -} - -/** - * Implements the "notify" effect. - * - * Dispatches a non-bubbling event named `info.eventName` on the instance - * with a detail object containing the new `value`. - * - * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on - * @param {string} property Name of property - * @param {Object} props Bag of current property changes - * @param {Object} oldProps Bag of previous values for changed properties - * @param {?} info Effect metadata - * @param {boolean} hasPaths True with `props` contains one or more paths - * @return {void} - * @private - */ -function runNotifyEffect(inst, property, props, oldProps, info, hasPaths) { - let rootProperty = hasPaths ? root(property) : property; - let path = rootProperty != property ? property : null; - let value = path ? get(inst, path) : inst.__data[property]; - if (path && value === undefined) { - value = props[property]; // specifically for .splices - } - dispatchNotifyEvent(inst, info.eventName, value, path); -} - -/** - * Handler function for 2-way notification events. Receives context - * information captured in the `addNotifyListener` closure from the - * `__notifyListeners` metadata. - * - * Sets the value of the notified property to the host property or path. If - * the event contained path information, translate that path to the host - * scope's name for that path first. - * - * @param {CustomEvent} event Notification event (e.g. '<property>-changed') - * @param {!Polymer_PropertyEffects} inst Host element instance handling the - * notification event - * @param {string} fromProp Child element property that was bound - * @param {string} toPath Host property/path that was bound - * @param {boolean} negate Whether the binding was negated - * @return {void} - * @private - */ -function handleNotification(event, inst, fromProp, toPath, negate) { - let value; - let detail = /** @type {Object} */(event.detail); - let fromPath = detail && detail.path; - if (fromPath) { - toPath = translate(fromProp, toPath, fromPath); - value = detail && detail.value; - } else { - value = event.currentTarget[fromProp]; - } - value = negate ? !value : value; - if (!inst[TYPES.READ_ONLY] || !inst[TYPES.READ_ONLY][toPath]) { - if (inst._setPendingPropertyOrPath(toPath, value, true, Boolean(fromPath)) - && (!detail || !detail.queueProperty)) { - inst._invalidateProperties(); - } - } -} - -/** - * Implements the "reflect" effect. - * - * Sets the attribute named `info.attrName` to the given property value. - * - * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on - * @param {string} property Name of property - * @param {Object} props Bag of current property changes - * @param {Object} oldProps Bag of previous values for changed properties - * @param {?} info Effect metadata - * @return {void} - * @private - */ -function runReflectEffect(inst, property, props, oldProps, info) { - let value = inst.__data[property]; - if (sanitizeDOMValue) { - value = sanitizeDOMValue(value, info.attrName, 'attribute', /** @type {Node} */(inst)); - } - inst._propertyToAttribute(property, info.attrName, value); -} - -/** - * Runs "computed" effects for a set of changed properties. - * - * This method differs from the generic `runEffects` method in that it - * continues to run computed effects based on the output of each pass until - * there are no more newly computed properties. This ensures that all - * properties that will be computed by the initial set of changes are - * computed before other effects (binding propagation, observers, and notify) - * run. - * - * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on - * @param {?Object} changedProps Bag of changed properties - * @param {?Object} oldProps Bag of previous values for changed properties - * @param {boolean} hasPaths True with `props` contains one or more paths - * @return {void} - * @private - */ -function runComputedEffects(inst, changedProps, oldProps, hasPaths) { - let computeEffects = inst[TYPES.COMPUTE]; - if (computeEffects) { - let inputProps = changedProps; - while (runEffects(inst, computeEffects, inputProps, oldProps, hasPaths)) { - Object.assign(/** @type {!Object} */ (oldProps), inst.__dataOld); - Object.assign(/** @type {!Object} */ (changedProps), inst.__dataPending); - inputProps = inst.__dataPending; - inst.__dataPending = null; - } - } -} - -/** - * Implements the "computed property" effect by running the method with the - * values of the arguments specified in the `info` object and setting the - * return value to the computed property specified. - * - * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on - * @param {string} property Name of property - * @param {?Object} props Bag of current property changes - * @param {?Object} oldProps Bag of previous values for changed properties - * @param {?} info Effect metadata - * @return {void} - * @private - */ -function runComputedEffect(inst, property, props, oldProps, info) { - let result = runMethodEffect(inst, property, props, oldProps, info); - let computedProp = info.methodInfo; - if (inst.__dataHasAccessor && inst.__dataHasAccessor[computedProp]) { - inst._setPendingProperty(computedProp, result, true); - } else { - inst[computedProp] = result; - } -} - -/** - * Computes path changes based on path links set up using the `linkPaths` - * API. - * - * @param {!Polymer_PropertyEffects} inst The instance whose props are changing - * @param {string} path Path that has changed - * @param {*} value Value of changed path - * @return {void} - * @private - */ -function computeLinkedPaths(inst, path, value) { - let links = inst.__dataLinkedPaths; - if (links) { - let link; - for (let a in links) { - let b = links[a]; - if (isDescendant(a, path)) { - link = translate(a, b, path); - inst._setPendingPropertyOrPath(link, value, true, true); - } else if (isDescendant(b, path)) { - link = translate(b, a, path); - inst._setPendingPropertyOrPath(link, value, true, true); - } - } - } -} - -// -- bindings ---------------------------------------------- - -/** - * Adds binding metadata to the current `nodeInfo`, and binding effects - * for all part dependencies to `templateInfo`. - * - * @param {Function} constructor Class that `_parseTemplate` is currently - * running on - * @param {TemplateInfo} templateInfo Template metadata for current template - * @param {NodeInfo} nodeInfo Node metadata for current template node - * @param {string} kind Binding kind, either 'property', 'attribute', or 'text' - * @param {string} target Target property name - * @param {!Array<!BindingPart>} parts Array of binding part metadata - * @param {string=} literal Literal text surrounding binding parts (specified - * only for 'property' bindings, since these must be initialized as part - * of boot-up) - * @return {void} - * @private - */ -function addBinding(constructor, templateInfo, nodeInfo, kind, target, parts, literal) { - // Create binding metadata and add to nodeInfo - nodeInfo.bindings = nodeInfo.bindings || []; - let /** Binding */ binding = { kind, target, parts, literal, isCompound: (parts.length !== 1) }; - nodeInfo.bindings.push(binding); - // Add listener info to binding metadata - if (shouldAddListener(binding)) { - let {event, negate} = binding.parts[0]; - binding.listenerEvent = event || (camelToDashCase(target) + '-changed'); - binding.listenerNegate = negate; - } - // Add "propagate" property effects to templateInfo - let index = templateInfo.nodeInfoList.length; - for (let i=0; i<binding.parts.length; i++) { - let part = binding.parts[i]; - part.compoundIndex = i; - addEffectForBindingPart(constructor, templateInfo, binding, part, index); - } -} - -/** - * Adds property effects to the given `templateInfo` for the given binding - * part. - * - * @param {Function} constructor Class that `_parseTemplate` is currently - * running on - * @param {TemplateInfo} templateInfo Template metadata for current template - * @param {!Binding} binding Binding metadata - * @param {!BindingPart} part Binding part metadata - * @param {number} index Index into `nodeInfoList` for this node - * @return {void} - */ -function addEffectForBindingPart(constructor, templateInfo, binding, part, index) { - if (!part.literal) { - if (binding.kind === 'attribute' && binding.target[0] === '-') { - console.warn('Cannot set attribute ' + binding.target + - ' because "-" is not a valid attribute starting character'); - } else { - let dependencies = part.dependencies; - let info = { index, binding, part, evaluator: constructor }; - for (let j=0; j<dependencies.length; j++) { - let trigger = dependencies[j]; - if (typeof trigger == 'string') { - trigger = parseArg(trigger); - trigger.wildcard = true; - } - constructor._addTemplatePropertyEffect(templateInfo, trigger.rootProperty, { - fn: runBindingEffect, - info, trigger - }); - } - } - } -} - -/** - * Implements the "binding" (property/path binding) effect. - * - * Note that binding syntax is overridable via `_parseBindings` and - * `_evaluateBinding`. This method will call `_evaluateBinding` for any - * non-literal parts returned from `_parseBindings`. However, - * there is no support for _path_ bindings via custom binding parts, - * as this is specific to Polymer's path binding syntax. - * - * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on - * @param {string} path Name of property - * @param {Object} props Bag of current property changes - * @param {Object} oldProps Bag of previous values for changed properties - * @param {?} info Effect metadata - * @param {boolean} hasPaths True with `props` contains one or more paths - * @param {Array} nodeList List of nodes associated with `nodeInfoList` template - * metadata - * @return {void} - * @private - */ -function runBindingEffect(inst, path, props, oldProps, info, hasPaths, nodeList) { - let node = nodeList[info.index]; - let binding = info.binding; - let part = info.part; - // Subpath notification: transform path and set to client - // e.g.: foo="{{obj.sub}}", path: 'obj.sub.prop', set 'foo.prop'=obj.sub.prop - if (hasPaths && part.source && (path.length > part.source.length) && - (binding.kind == 'property') && !binding.isCompound && - node.__isPropertyEffectsClient && - node.__dataHasAccessor && node.__dataHasAccessor[binding.target]) { - let value = props[path]; - path = translate(part.source, binding.target, path); - if (node._setPendingPropertyOrPath(path, value, false, true)) { - inst._enqueueClient(node); - } - } else { - let value = info.evaluator._evaluateBinding(inst, part, path, props, oldProps, hasPaths); - // Propagate value to child - applyBindingValue(inst, node, binding, part, value); - } -} - -/** - * Sets the value for an "binding" (binding) effect to a node, - * either as a property or attribute. - * - * @param {!Polymer_PropertyEffects} inst The instance owning the binding effect - * @param {Node} node Target node for binding - * @param {!Binding} binding Binding metadata - * @param {!BindingPart} part Binding part metadata - * @param {*} value Value to set - * @return {void} - * @private - */ -function applyBindingValue(inst, node, binding, part, value) { - value = computeBindingValue(node, value, binding, part); - if (sanitizeDOMValue) { - value = sanitizeDOMValue(value, binding.target, binding.kind, node); - } - if (binding.kind == 'attribute') { - // Attribute binding - inst._valueToNodeAttribute(/** @type {Element} */(node), value, binding.target); - } else { - // Property binding - let prop = binding.target; - if (node.__isPropertyEffectsClient && - node.__dataHasAccessor && node.__dataHasAccessor[prop]) { - if (!node[TYPES.READ_ONLY] || !node[TYPES.READ_ONLY][prop]) { - if (node._setPendingProperty(prop, value)) { - inst._enqueueClient(node); - } - } - } else { - inst._setUnmanagedPropertyToNode(node, prop, value); - } - } -} - -/** - * Transforms an "binding" effect value based on compound & negation - * effect metadata, as well as handling for special-case properties - * - * @param {Node} node Node the value will be set to - * @param {*} value Value to set - * @param {!Binding} binding Binding metadata - * @param {!BindingPart} part Binding part metadata - * @return {*} Transformed value to set - * @private - */ -function computeBindingValue(node, value, binding, part) { - if (binding.isCompound) { - let storage = node.__dataCompoundStorage[binding.target]; - storage[part.compoundIndex] = value; - value = storage.join(''); - } - if (binding.kind !== 'attribute') { - // Some browsers serialize `undefined` to `"undefined"` - if (binding.target === 'textContent' || - (binding.target === 'value' && - (node.localName === 'input' || node.localName === 'textarea'))) { - value = value == undefined ? '' : value; - } - } - return value; -} - -/** - * Returns true if a binding's metadata meets all the requirements to allow - * 2-way binding, and therefore a `<property>-changed` event listener should be - * added: - * - used curly braces - * - is a property (not attribute) binding - * - is not a textContent binding - * - is not compound - * - * @param {!Binding} binding Binding metadata - * @return {boolean} True if 2-way listener should be added - * @private - */ -function shouldAddListener(binding) { - return Boolean(binding.target) && - binding.kind != 'attribute' && - binding.kind != 'text' && - !binding.isCompound && - binding.parts[0].mode === '{'; -} - -/** - * Setup compound binding storage structures, notify listeners, and dataHost - * references onto the bound nodeList. - * - * @param {!Polymer_PropertyEffects} inst Instance that bas been previously - * bound - * @param {TemplateInfo} templateInfo Template metadata - * @return {void} - * @private - */ -function setupBindings(inst, templateInfo) { - // Setup compound storage, dataHost, and notify listeners - let {nodeList, nodeInfoList} = templateInfo; - if (nodeInfoList.length) { - for (let i=0; i < nodeInfoList.length; i++) { - let info = nodeInfoList[i]; - let node = nodeList[i]; - let bindings = info.bindings; - if (bindings) { - for (let i=0; i<bindings.length; i++) { - let binding = bindings[i]; - setupCompoundStorage(node, binding); - addNotifyListener(node, inst, binding); - } - } - node.__dataHost = inst; - } - } -} - -/** - * Initializes `__dataCompoundStorage` local storage on a bound node with - * initial literal data for compound bindings, and sets the joined - * literal parts to the bound property. - * - * When changes to compound parts occur, they are first set into the compound - * storage array for that property, and then the array is joined to result in - * the final value set to the property/attribute. - * - * @param {Node} node Bound node to initialize - * @param {Binding} binding Binding metadata - * @return {void} - * @private - */ -function setupCompoundStorage(node, binding) { - if (binding.isCompound) { - // Create compound storage map - let storage = node.__dataCompoundStorage || - (node.__dataCompoundStorage = {}); - let parts = binding.parts; - // Copy literals from parts into storage for this binding - let literals = new Array(parts.length); - for (let j=0; j<parts.length; j++) { - literals[j] = parts[j].literal; - } - let target = binding.target; - storage[target] = literals; - // Configure properties with their literal parts - if (binding.literal && binding.kind == 'property') { - node[target] = binding.literal; - } - } -} - -/** - * Adds a 2-way binding notification event listener to the node specified - * - * @param {Object} node Child element to add listener to - * @param {!Polymer_PropertyEffects} inst Host element instance to handle - * notification event - * @param {Binding} binding Binding metadata - * @return {void} - * @private - */ -function addNotifyListener(node, inst, binding) { - if (binding.listenerEvent) { - let part = binding.parts[0]; - node.addEventListener(binding.listenerEvent, function(e) { - handleNotification(e, inst, binding.target, part.source, part.negate); - }); - } -} - -// -- for method-based effects (complexObserver & computed) -------------- - -/** - * Adds property effects for each argument in the method signature (and - * optionally, for the method name if `dynamic` is true) that calls the - * provided effect function. - * - * @param {Element | Object} model Prototype or instance - * @param {!MethodSignature} sig Method signature metadata - * @param {string} type Type of property effect to add - * @param {Function} effectFn Function to run when arguments change - * @param {*=} methodInfo Effect-specific information to be included in - * method effect metadata - * @param {boolean|Object=} dynamicFn Boolean or object map indicating whether - * method names should be included as a dependency to the effect. Note, - * defaults to true if the signature is static (sig.static is true). - * @return {void} - * @private - */ -function createMethodEffect(model, sig, type, effectFn, methodInfo, dynamicFn) { - dynamicFn = sig.static || (dynamicFn && - (typeof dynamicFn !== 'object' || dynamicFn[sig.methodName])); - let info = { - methodName: sig.methodName, - args: sig.args, - methodInfo, - dynamicFn - }; - for (let i=0, arg; (i<sig.args.length) && (arg=sig.args[i]); i++) { - if (!arg.literal) { - model._addPropertyEffect(arg.rootProperty, type, { - fn: effectFn, info: info, trigger: arg - }); - } - } - if (dynamicFn) { - model._addPropertyEffect(sig.methodName, type, { - fn: effectFn, info: info - }); - } -} - -/** - * Calls a method with arguments marshaled from properties on the instance - * based on the method signature contained in the effect metadata. - * - * Multi-property observers, computed properties, and inline computing - * functions call this function to invoke the method, then use the return - * value accordingly. - * - * @param {!Polymer_PropertyEffects} inst The instance the effect will be run on - * @param {string} property Name of property - * @param {Object} props Bag of current property changes - * @param {Object} oldProps Bag of previous values for changed properties - * @param {?} info Effect metadata - * @return {*} Returns the return value from the method invocation - * @private - */ -function runMethodEffect(inst, property, props, oldProps, info) { - // Instances can optionally have a _methodHost which allows redirecting where - // to find methods. Currently used by `templatize`. - let context = inst._methodHost || inst; - let fn = context[info.methodName]; - if (fn) { - let args = inst._marshalArgs(info.args, property, props); - return fn.apply(context, args); - } else if (!info.dynamicFn) { - console.warn('method `' + info.methodName + '` not defined'); - } -} - -const emptyArray = []; - -// Regular expressions used for binding -const IDENT = '(?:' + '[a-zA-Z_$][\\w.:$\\-*]*' + ')'; -const NUMBER = '(?:' + '[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?' + ')'; -const SQUOTE_STRING = '(?:' + '\'(?:[^\'\\\\]|\\\\.)*\'' + ')'; -const DQUOTE_STRING = '(?:' + '"(?:[^"\\\\]|\\\\.)*"' + ')'; -const STRING = '(?:' + SQUOTE_STRING + '|' + DQUOTE_STRING + ')'; -const ARGUMENT = '(?:(' + IDENT + '|' + NUMBER + '|' + STRING + ')\\s*' + ')'; -const ARGUMENTS = '(?:' + ARGUMENT + '(?:,\\s*' + ARGUMENT + ')*' + ')'; -const ARGUMENT_LIST = '(?:' + '\\(\\s*' + - '(?:' + ARGUMENTS + '?' + ')' + - '\\)\\s*' + ')'; -const BINDING = '(' + IDENT + '\\s*' + ARGUMENT_LIST + '?' + ')'; // Group 3 -const OPEN_BRACKET = '(\\[\\[|{{)' + '\\s*'; -const CLOSE_BRACKET = '(?:]]|}})'; -const NEGATE = '(?:(!)\\s*)?'; // Group 2 -const EXPRESSION = OPEN_BRACKET + NEGATE + BINDING + CLOSE_BRACKET; -const bindingRegex = new RegExp(EXPRESSION, "g"); - -/** - * Create a string from binding parts of all the literal parts - * - * @param {!Array<BindingPart>} parts All parts to stringify - * @return {string} String made from the literal parts - */ -function literalFromParts(parts) { - let s = ''; - for (let i=0; i<parts.length; i++) { - let literal = parts[i].literal; - s += literal || ''; - } - return s; -} - -/** - * Parses an expression string for a method signature, and returns a metadata - * describing the method in terms of `methodName`, `static` (whether all the - * arguments are literals), and an array of `args` - * - * @param {string} expression The expression to parse - * @return {?MethodSignature} The method metadata object if a method expression was - * found, otherwise `undefined` - * @private - */ -function parseMethod(expression) { - // tries to match valid javascript property names - let m = expression.match(/([^\s]+?)\(([\s\S]*)\)/); - if (m) { - let methodName = m[1]; - let sig = { methodName, static: true, args: emptyArray }; - if (m[2].trim()) { - // replace escaped commas with comma entity, split on un-escaped commas - let args = m[2].replace(/\\,/g, ',').split(','); - return parseArgs(args, sig); - } else { - return sig; - } - } - return null; -} - -/** - * Parses an array of arguments and sets the `args` property of the supplied - * signature metadata object. Sets the `static` property to false if any - * argument is a non-literal. - * - * @param {!Array<string>} argList Array of argument names - * @param {!MethodSignature} sig Method signature metadata object - * @return {!MethodSignature} The updated signature metadata object - * @private - */ -function parseArgs(argList, sig) { - sig.args = argList.map(function(rawArg) { - let arg = parseArg(rawArg); - if (!arg.literal) { - sig.static = false; - } - return arg; - }, this); - return sig; -} - -/** - * Parses an individual argument, and returns an argument metadata object - * with the following fields: - * - * { - * value: 'prop', // property/path or literal value - * literal: false, // whether argument is a literal - * structured: false, // whether the property is a path - * rootProperty: 'prop', // the root property of the path - * wildcard: false // whether the argument was a wildcard '.*' path - * } - * - * @param {string} rawArg The string value of the argument - * @return {!MethodArg} Argument metadata object - * @private - */ -function parseArg(rawArg) { - // clean up whitespace - let arg = rawArg.trim() - // replace comma entity with comma - .replace(/,/g, ',') - // repair extra escape sequences; note only commas strictly need - // escaping, but we allow any other char to be escaped since its - // likely users will do this - .replace(/\\(.)/g, '\$1') - ; - // basic argument descriptor - let a = { - name: arg, - value: '', - literal: false - }; - // detect literal value (must be String or Number) - let fc = arg[0]; - if (fc === '-') { - fc = arg[1]; - } - if (fc >= '0' && fc <= '9') { - fc = '#'; - } - switch(fc) { - case "'": - case '"': - a.value = arg.slice(1, -1); - a.literal = true; - break; - case '#': - a.value = Number(arg); - a.literal = true; - break; - } - // if not literal, look for structured path - if (!a.literal) { - a.rootProperty = root(arg); - // detect structured path (has dots) - a.structured = isPath(arg); - if (a.structured) { - a.wildcard = (arg.slice(-2) == '.*'); - if (a.wildcard) { - a.name = arg.slice(0, -2); - } - } - } - return a; -} - -function getArgValue(data, props, path) { - let value = get(data, path); - // when data is not stored e.g. `splices`, get the value from changedProps - // TODO(kschaaf): Note, this can cause a rare issue where the wildcard - // info.value could pull a stale value out of changedProps during a reentrant - // change that sets the value back to undefined. - // https://github.com/Polymer/polymer/issues/5479 - if (value === undefined) { - value = props[path]; - } - return value; -} - -// data api - -/** - * Sends array splice notifications (`.splices` and `.length`) - * - * Note: this implementation only accepts normalized paths - * - * @param {!Polymer_PropertyEffects} inst Instance to send notifications to - * @param {Array} array The array the mutations occurred on - * @param {string} path The path to the array that was mutated - * @param {Array} splices Array of splice records - * @return {void} - * @private - */ -function notifySplices(inst, array, path, splices) { - inst.notifyPath(path + '.splices', { indexSplices: splices }); - inst.notifyPath(path + '.length', array.length); -} - -/** - * Creates a splice record and sends an array splice notification for - * the described mutation - * - * Note: this implementation only accepts normalized paths - * - * @param {!Polymer_PropertyEffects} inst Instance to send notifications to - * @param {Array} array The array the mutations occurred on - * @param {string} path The path to the array that was mutated - * @param {number} index Index at which the array mutation occurred - * @param {number} addedCount Number of added items - * @param {Array} removed Array of removed items - * @return {void} - * @private - */ -function notifySplice(inst, array, path, index, addedCount, removed) { - notifySplices(inst, array, path, [{ - index: index, - addedCount: addedCount, - removed: removed, - object: array, - type: 'splice' - }]); -} - -/** - * Returns an upper-cased version of the string. - * - * @param {string} name String to uppercase - * @return {string} Uppercased string - * @private - */ -function upper(name) { - return name[0].toUpperCase() + name.substring(1); -} - -/** - * Element class mixin that provides meta-programming for Polymer's template - * binding and data observation (collectively, "property effects") system. - * - * This mixin uses provides the following key static methods for adding - * property effects to an element class: - * - `addPropertyEffect` - * - `createPropertyObserver` - * - `createMethodObserver` - * - `createNotifyingProperty` - * - `createReadOnlyProperty` - * - `createReflectedProperty` - * - `createComputedProperty` - * - `bindTemplate` - * - * Each method creates one or more property accessors, along with metadata - * used by this mixin's implementation of `_propertiesChanged` to perform - * the property effects. - * - * Underscored versions of the above methods also exist on the element - * prototype for adding property effects on instances at runtime. - * - * Note that this mixin overrides several `PropertyAccessors` methods, in - * many cases to maintain guarantees provided by the Polymer 1.x features; - * notably it changes property accessors to be synchronous by default - * whereas the default when using `PropertyAccessors` standalone is to be - * async by default. - * - * @mixinFunction - * @polymer - * @appliesMixin TemplateStamp - * @appliesMixin PropertyAccessors - * @summary Element class mixin that provides meta-programming for Polymer's - * template binding and data observation system. - */ -export const PropertyEffects = dedupingMixin(superClass => { - - /** - * @constructor - * @implements {Polymer_PropertyAccessors} - * @implements {Polymer_TemplateStamp} - * @unrestricted - * @private - */ - const propertyEffectsBase = TemplateStamp(PropertyAccessors(superClass)); - - /** - * @polymer - * @mixinClass - * @implements {Polymer_PropertyEffects} - * @extends {propertyEffectsBase} - * @unrestricted - */ - class PropertyEffects extends propertyEffectsBase { - - constructor() { - super(); - /** @type {boolean} */ - // Used to identify users of this mixin, ala instanceof - this.__isPropertyEffectsClient = true; - /** @type {number} */ - // NOTE: used to track re-entrant calls to `_flushProperties` - // path changes dirty check against `__dataTemp` only during one "turn" - // and are cleared when `__dataCounter` returns to 0. - this.__dataCounter = 0; - /** @type {boolean} */ - this.__dataClientsReady; - /** @type {Array} */ - this.__dataPendingClients; - /** @type {Object} */ - this.__dataToNotify; - /** @type {Object} */ - this.__dataLinkedPaths; - /** @type {boolean} */ - this.__dataHasPaths; - /** @type {Object} */ - this.__dataCompoundStorage; - /** @type {Polymer_PropertyEffects} */ - this.__dataHost; - /** @type {!Object} */ - this.__dataTemp; - /** @type {boolean} */ - this.__dataClientsInitialized; - /** @type {!Object} */ - this.__data; - /** @type {!Object|null} */ - this.__dataPending; - /** @type {!Object} */ - this.__dataOld; - /** @type {Object} */ - this.__computeEffects; - /** @type {Object} */ - this.__reflectEffects; - /** @type {Object} */ - this.__notifyEffects; - /** @type {Object} */ - this.__propagateEffects; - /** @type {Object} */ - this.__observeEffects; - /** @type {Object} */ - this.__readOnly; - /** @type {!TemplateInfo} */ - this.__templateInfo; - } - - get PROPERTY_EFFECT_TYPES() { - return TYPES; - } - - /** - * @override - * @return {void} - */ - _initializeProperties() { - super._initializeProperties(); - hostStack.registerHost(this); - this.__dataClientsReady = false; - this.__dataPendingClients = null; - this.__dataToNotify = null; - this.__dataLinkedPaths = null; - this.__dataHasPaths = false; - // May be set on instance prior to upgrade - this.__dataCompoundStorage = this.__dataCompoundStorage || null; - this.__dataHost = this.__dataHost || null; - this.__dataTemp = {}; - this.__dataClientsInitialized = false; - } - - /** - * Overrides `PropertyAccessors` implementation to provide a - * more efficient implementation of initializing properties from - * the prototype on the instance. - * - * @override - * @param {Object} props Properties to initialize on the prototype - * @return {void} - */ - _initializeProtoProperties(props) { - this.__data = Object.create(props); - this.__dataPending = Object.create(props); - this.__dataOld = {}; - } - - /** - * Overrides `PropertyAccessors` implementation to avoid setting - * `_setProperty`'s `shouldNotify: true`. - * - * @override - * @param {Object} props Properties to initialize on the instance - * @return {void} - */ - _initializeInstanceProperties(props) { - let readOnly = this[TYPES.READ_ONLY]; - for (let prop in props) { - if (!readOnly || !readOnly[prop]) { - this.__dataPending = this.__dataPending || {}; - this.__dataOld = this.__dataOld || {}; - this.__data[prop] = this.__dataPending[prop] = props[prop]; - } - } - } - - // Prototype setup ---------------------------------------- - - /** - * Equivalent to static `addPropertyEffect` API but can be called on - * an instance to add effects at runtime. See that method for - * full API docs. - * - * @override - * @param {string} property Property that should trigger the effect - * @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES - * @param {Object=} effect Effect metadata object - * @return {void} - * @protected - */ - _addPropertyEffect(property, type, effect) { - this._createPropertyAccessor(property, type == TYPES.READ_ONLY); - // effects are accumulated into arrays per property based on type - let effects = ensureOwnEffectMap(this, type)[property]; - if (!effects) { - effects = this[type][property] = []; - } - effects.push(effect); - } - - /** - * Removes the given property effect. - * - * @override - * @param {string} property Property the effect was associated with - * @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES - * @param {Object=} effect Effect metadata object to remove - * @return {void} - */ - _removePropertyEffect(property, type, effect) { - let effects = ensureOwnEffectMap(this, type)[property]; - let idx = effects.indexOf(effect); - if (idx >= 0) { - effects.splice(idx, 1); - } - } - - /** - * Returns whether the current prototype/instance has a property effect - * of a certain type. - * - * @override - * @param {string} property Property name - * @param {string=} type Effect type, from this.PROPERTY_EFFECT_TYPES - * @return {boolean} True if the prototype/instance has an effect of this - * type - * @protected - */ - _hasPropertyEffect(property, type) { - let effects = this[type]; - return Boolean(effects && effects[property]); - } - - /** - * Returns whether the current prototype/instance has a "read only" - * accessor for the given property. - * - * @override - * @param {string} property Property name - * @return {boolean} True if the prototype/instance has an effect of this - * type - * @protected - */ - _hasReadOnlyEffect(property) { - return this._hasPropertyEffect(property, TYPES.READ_ONLY); - } - - /** - * Returns whether the current prototype/instance has a "notify" - * property effect for the given property. - * - * @override - * @param {string} property Property name - * @return {boolean} True if the prototype/instance has an effect of this - * type - * @protected - */ - _hasNotifyEffect(property) { - return this._hasPropertyEffect(property, TYPES.NOTIFY); - } - - /** - * Returns whether the current prototype/instance has a "reflect to - * attribute" property effect for the given property. - * - * @override - * @param {string} property Property name - * @return {boolean} True if the prototype/instance has an effect of this - * type - * @protected - */ - _hasReflectEffect(property) { - return this._hasPropertyEffect(property, TYPES.REFLECT); - } - - /** - * Returns whether the current prototype/instance has a "computed" - * property effect for the given property. - * - * @override - * @param {string} property Property name - * @return {boolean} True if the prototype/instance has an effect of this - * type - * @protected - */ - _hasComputedEffect(property) { - return this._hasPropertyEffect(property, TYPES.COMPUTE); - } - - // Runtime ---------------------------------------- - - /** - * Sets a pending property or path. If the root property of the path in - * question had no accessor, the path is set, otherwise it is enqueued - * via `_setPendingProperty`. - * - * This function isolates relatively expensive functionality necessary - * for the public API (`set`, `setProperties`, `notifyPath`, and property - * change listeners via {{...}} bindings), such that it is only done - * when paths enter the system, and not at every propagation step. It - * also sets a `__dataHasPaths` flag on the instance which is used to - * fast-path slower path-matching code in the property effects host paths. - * - * `path` can be a path string or array of path parts as accepted by the - * public API. - * - * @override - * @param {string | !Array<number|string>} path Path to set - * @param {*} value Value to set - * @param {boolean=} shouldNotify Set to true if this change should - * cause a property notification event dispatch - * @param {boolean=} isPathNotification If the path being set is a path - * notification of an already changed value, as opposed to a request - * to set and notify the change. In the latter `false` case, a dirty - * check is performed and then the value is set to the path before - * enqueuing the pending property change. - * @return {boolean} Returns true if the property/path was enqueued in - * the pending changes bag. - * @protected - */ - _setPendingPropertyOrPath(path, value, shouldNotify, isPathNotification) { - if (isPathNotification || - root(Array.isArray(path) ? path[0] : path) !== path) { - // Dirty check changes being set to a path against the actual object, - // since this is the entry point for paths into the system; from here - // the only dirty checks are against the `__dataTemp` cache to prevent - // duplicate work in the same turn only. Note, if this was a notification - // of a change already set to a path (isPathNotification: true), - // we always let the change through and skip the `set` since it was - // already dirty checked at the point of entry and the underlying - // object has already been updated - if (!isPathNotification) { - let old = get(this, path); - path = /** @type {string} */ (set(this, path, value)); - // Use property-accessor's simpler dirty check - if (!path || !super._shouldPropertyChange(path, value, old)) { - return false; - } - } - this.__dataHasPaths = true; - if (this._setPendingProperty(/**@type{string}*/(path), value, shouldNotify)) { - computeLinkedPaths(this, /**@type{string}*/ (path), value); - return true; - } - } else { - if (this.__dataHasAccessor && this.__dataHasAccessor[path]) { - return this._setPendingProperty(/**@type{string}*/(path), value, shouldNotify); - } else { - this[path] = value; - } - } - return false; - } - - /** - * Applies a value to a non-Polymer element/node's property. - * - * The implementation makes a best-effort at binding interop: - * Some native element properties have side-effects when - * re-setting the same value (e.g. setting `<input>.value` resets the - * cursor position), so we do a dirty-check before setting the value. - * However, for better interop with non-Polymer custom elements that - * accept objects, we explicitly re-set object changes coming from the - * Polymer world (which may include deep object changes without the - * top reference changing), erring on the side of providing more - * information. - * - * Users may override this method to provide alternate approaches. - * - * @override - * @param {!Node} node The node to set a property on - * @param {string} prop The property to set - * @param {*} value The value to set - * @return {void} - * @protected - */ - _setUnmanagedPropertyToNode(node, prop, value) { - // It is a judgment call that resetting primitives is - // "bad" and resettings objects is also "good"; alternatively we could - // implement a whitelist of tag & property values that should never - // be reset (e.g. <input>.value && <select>.value) - if (value !== node[prop] || typeof value == 'object') { - node[prop] = value; - } - } - - /** - * Overrides the `PropertiesChanged` implementation to introduce special - * dirty check logic depending on the property & value being set: - * - * 1. Any value set to a path (e.g. 'obj.prop': 42 or 'obj.prop': {...}) - * Stored in `__dataTemp`, dirty checked against `__dataTemp` - * 2. Object set to simple property (e.g. 'prop': {...}) - * Stored in `__dataTemp` and `__data`, dirty checked against - * `__dataTemp` by default implementation of `_shouldPropertyChange` - * 3. Primitive value set to simple property (e.g. 'prop': 42) - * Stored in `__data`, dirty checked against `__data` - * - * The dirty-check is important to prevent cycles due to two-way - * notification, but paths and objects are only dirty checked against any - * previous value set during this turn via a "temporary cache" that is - * cleared when the last `_propertiesChanged` exits. This is so: - * a. any cached array paths (e.g. 'array.3.prop') may be invalidated - * due to array mutations like shift/unshift/splice; this is fine - * since path changes are dirty-checked at user entry points like `set` - * b. dirty-checking for objects only lasts one turn to allow the user - * to mutate the object in-place and re-set it with the same identity - * and have all sub-properties re-propagated in a subsequent turn. - * - * The temp cache is not necessarily sufficient to prevent invalid array - * paths, since a splice can happen during the same turn (with pathological - * user code); we could introduce a "fixup" for temporarily cached array - * paths if needed: https://github.com/Polymer/polymer/issues/4227 - * - * @override - * @param {string} property Name of the property - * @param {*} value Value to set - * @param {boolean=} shouldNotify True if property should fire notification - * event (applies only for `notify: true` properties) - * @return {boolean} Returns true if the property changed - */ - _setPendingProperty(property, value, shouldNotify) { - let propIsPath = this.__dataHasPaths && isPath(property); - let prevProps = propIsPath ? this.__dataTemp : this.__data; - if (this._shouldPropertyChange(property, value, prevProps[property])) { - if (!this.__dataPending) { - this.__dataPending = {}; - this.__dataOld = {}; - } - // Ensure old is captured from the last turn - if (!(property in this.__dataOld)) { - this.__dataOld[property] = this.__data[property]; - } - // Paths are stored in temporary cache (cleared at end of turn), - // which is used for dirty-checking, all others stored in __data - if (propIsPath) { - this.__dataTemp[property] = value; - } else { - this.__data[property] = value; - } - // All changes go into pending property bag, passed to _propertiesChanged - this.__dataPending[property] = value; - // Track properties that should notify separately - if (propIsPath || (this[TYPES.NOTIFY] && this[TYPES.NOTIFY][property])) { - this.__dataToNotify = this.__dataToNotify || {}; - this.__dataToNotify[property] = shouldNotify; - } - return true; - } - return false; - } - - /** - * Overrides base implementation to ensure all accessors set `shouldNotify` - * to true, for per-property notification tracking. - * - * @override - * @param {string} property Name of the property - * @param {*} value Value to set - * @return {void} - */ - _setProperty(property, value) { - if (this._setPendingProperty(property, value, true)) { - this._invalidateProperties(); - } - } - - /** - * Overrides `PropertyAccessor`'s default async queuing of - * `_propertiesChanged`: if `__dataReady` is false (has not yet been - * manually flushed), the function no-ops; otherwise flushes - * `_propertiesChanged` synchronously. - * - * @override - * @return {void} - */ - _invalidateProperties() { - if (this.__dataReady) { - this._flushProperties(); - } - } - - /** - * Enqueues the given client on a list of pending clients, whose - * pending property changes can later be flushed via a call to - * `_flushClients`. - * - * @override - * @param {Object} client PropertyEffects client to enqueue - * @return {void} - * @protected - */ - _enqueueClient(client) { - this.__dataPendingClients = this.__dataPendingClients || []; - if (client !== this) { - this.__dataPendingClients.push(client); - } - } - - /** - * Overrides superclass implementation. - * - * @override - * @return {void} - * @protected - */ - _flushProperties() { - this.__dataCounter++; - super._flushProperties(); - this.__dataCounter--; - } - - /** - * Flushes any clients previously enqueued via `_enqueueClient`, causing - * their `_flushProperties` method to run. - * - * @override - * @return {void} - * @protected - */ - _flushClients() { - if (!this.__dataClientsReady) { - this.__dataClientsReady = true; - this._readyClients(); - // Override point where accessors are turned on; importantly, - // this is after clients have fully readied, providing a guarantee - // that any property effects occur only after all clients are ready. - this.__dataReady = true; - } else { - this.__enableOrFlushClients(); - } - } - - // NOTE: We ensure clients either enable or flush as appropriate. This - // handles two corner cases: - // (1) clients flush properly when connected/enabled before the host - // enables; e.g. - // (a) Templatize stamps with no properties and does not flush and - // (b) the instance is inserted into dom and - // (c) then the instance flushes. - // (2) clients enable properly when not connected/enabled when the host - // flushes; e.g. - // (a) a template is runtime stamped and not yet connected/enabled - // (b) a host sets a property, causing stamped dom to flush - // (c) the stamped dom enables. - __enableOrFlushClients() { - let clients = this.__dataPendingClients; - if (clients) { - this.__dataPendingClients = null; - for (let i=0; i < clients.length; i++) { - let client = clients[i]; - if (!client.__dataEnabled) { - client._enableProperties(); - } else if (client.__dataPending) { - client._flushProperties(); - } - } - } - } - - /** - * Perform any initial setup on client dom. Called before the first - * `_flushProperties` call on client dom and before any element - * observers are called. - * - * @override - * @return {void} - * @protected - */ - _readyClients() { - this.__enableOrFlushClients(); - } - - /** - * Sets a bag of property changes to this instance, and - * synchronously processes all effects of the properties as a batch. - * - * Property names must be simple properties, not paths. Batched - * path propagation is not supported. - * - * @override - * @param {Object} props Bag of one or more key-value pairs whose key is - * a property and value is the new value to set for that property. - * @param {boolean=} setReadOnly When true, any private values set in - * `props` will be set. By default, `setProperties` will not set - * `readOnly: true` root properties. - * @return {void} - * @public - */ - setProperties(props, setReadOnly) { - for (let path in props) { - if (setReadOnly || !this[TYPES.READ_ONLY] || !this[TYPES.READ_ONLY][path]) { - //TODO(kschaaf): explicitly disallow paths in setProperty? - // wildcard observers currently only pass the first changed path - // in the `info` object, and you could do some odd things batching - // paths, e.g. {'foo.bar': {...}, 'foo': null} - this._setPendingPropertyOrPath(path, props[path], true); - } - } - this._invalidateProperties(); - } - - /** - * Overrides `PropertyAccessors` so that property accessor - * side effects are not enabled until after client dom is fully ready. - * Also calls `_flushClients` callback to ensure client dom is enabled - * that was not enabled as a result of flushing properties. - * - * @override - * @return {void} - */ - ready() { - // It is important that `super.ready()` is not called here as it - // immediately turns on accessors. Instead, we wait until `readyClients` - // to enable accessors to provide a guarantee that clients are ready - // before processing any accessors side effects. - this._flushProperties(); - // If no data was pending, `_flushProperties` will not `flushClients` - // so ensure this is done. - if (!this.__dataClientsReady) { - this._flushClients(); - } - // Before ready, client notifications do not trigger _flushProperties. - // Therefore a flush is necessary here if data has been set. - if (this.__dataPending) { - this._flushProperties(); - } - } - - /** - * Implements `PropertyAccessors`'s properties changed callback. - * - * Runs each class of effects for the batch of changed properties in - * a specific order (compute, propagate, reflect, observe, notify). - * - * @override - * @param {!Object} currentProps Bag of all current accessor values - * @param {?Object} changedProps Bag of properties changed since the last - * call to `_propertiesChanged` - * @param {?Object} oldProps Bag of previous values for each property - * in `changedProps` - * @return {void} - */ - _propertiesChanged(currentProps, changedProps, oldProps) { - // ---------------------------- - // let c = Object.getOwnPropertyNames(changedProps || {}); - // window.debug && console.group(this.localName + '#' + this.id + ': ' + c); - // if (window.debug) { debugger; } - // ---------------------------- - let hasPaths = this.__dataHasPaths; - this.__dataHasPaths = false; - // Compute properties - runComputedEffects(this, changedProps, oldProps, hasPaths); - // Clear notify properties prior to possible reentry (propagate, observe), - // but after computing effects have a chance to add to them - let notifyProps = this.__dataToNotify; - this.__dataToNotify = null; - // Propagate properties to clients - this._propagatePropertyChanges(changedProps, oldProps, hasPaths); - // Flush clients - this._flushClients(); - // Reflect properties - runEffects(this, this[TYPES.REFLECT], changedProps, oldProps, hasPaths); - // Observe properties - runEffects(this, this[TYPES.OBSERVE], changedProps, oldProps, hasPaths); - // Notify properties to host - if (notifyProps) { - runNotifyEffects(this, notifyProps, changedProps, oldProps, hasPaths); - } - // Clear temporary cache at end of turn - if (this.__dataCounter == 1) { - this.__dataTemp = {}; - } - // ---------------------------- - // window.debug && console.groupEnd(this.localName + '#' + this.id + ': ' + c); - // ---------------------------- - } - - /** - * Called to propagate any property changes to stamped template nodes - * managed by this element. - * - * @override - * @param {Object} changedProps Bag of changed properties - * @param {Object} oldProps Bag of previous values for changed properties - * @param {boolean} hasPaths True with `props` contains one or more paths - * @return {void} - * @protected - */ - _propagatePropertyChanges(changedProps, oldProps, hasPaths) { - if (this[TYPES.PROPAGATE]) { - runEffects(this, this[TYPES.PROPAGATE], changedProps, oldProps, hasPaths); - } - let templateInfo = this.__templateInfo; - while (templateInfo) { - runEffects(this, templateInfo.propertyEffects, changedProps, oldProps, - hasPaths, templateInfo.nodeList); - templateInfo = templateInfo.nextTemplateInfo; - } - } - - /** - * Aliases one data path as another, such that path notifications from one - * are routed to the other. - * - * @override - * @param {string | !Array<string|number>} to Target path to link. - * @param {string | !Array<string|number>} from Source path to link. - * @return {void} - * @public - */ - linkPaths(to, from) { - to = normalize(to); - from = normalize(from); - this.__dataLinkedPaths = this.__dataLinkedPaths || {}; - this.__dataLinkedPaths[to] = from; - } - - /** - * Removes a data path alias previously established with `_linkPaths`. - * - * Note, the path to unlink should be the target (`to`) used when - * linking the paths. - * - * @override - * @param {string | !Array<string|number>} path Target path to unlink. - * @return {void} - * @public - */ - unlinkPaths(path) { - path = normalize(path); - if (this.__dataLinkedPaths) { - delete this.__dataLinkedPaths[path]; - } - } - - /** - * Notify that an array has changed. - * - * Example: - * - * this.items = [ {name: 'Jim'}, {name: 'Todd'}, {name: 'Bill'} ]; - * ... - * this.items.splice(1, 1, {name: 'Sam'}); - * this.items.push({name: 'Bob'}); - * this.notifySplices('items', [ - * { index: 1, removed: [{name: 'Todd'}], addedCount: 1, - * object: this.items, type: 'splice' }, - * { index: 3, removed: [], addedCount: 1, - * object: this.items, type: 'splice'} - * ]); - * - * @param {string} path Path that should be notified. - * @param {Array} splices Array of splice records indicating ordered - * changes that occurred to the array. Each record should have the - * following fields: - * * index: index at which the change occurred - * * removed: array of items that were removed from this index - * * addedCount: number of new items added at this index - * * object: a reference to the array in question - * * type: the string literal 'splice' - * - * Note that splice records _must_ be normalized such that they are - * reported in index order (raw results from `Object.observe` are not - * ordered and must be normalized/merged before notifying). - * - * @override - * @return {void} - * @public - */ - notifySplices(path, splices) { - let info = {path: ''}; - let array = /** @type {Array} */(get(this, path, info)); - notifySplices(this, array, info.path, splices); - } - - /** - * Convenience method for reading a value from a path. - * - * Note, if any part in the path is undefined, this method returns - * `undefined` (this method does not throw when dereferencing undefined - * paths). - * - * @override - * @param {(string|!Array<(string|number)>)} path Path to the value - * to read. The path may be specified as a string (e.g. `foo.bar.baz`) - * or an array of path parts (e.g. `['foo.bar', 'baz']`). Note that - * bracketed expressions are not supported; string-based path parts - * *must* be separated by dots. Note that when dereferencing array - * indices, the index may be used as a dotted part directly - * (e.g. `users.12.name` or `['users', 12, 'name']`). - * @param {Object=} root Root object from which the path is evaluated. - * @return {*} Value at the path, or `undefined` if any part of the path - * is undefined. - * @public - */ - get(path, root) { - return get(root || this, path); - } - - /** - * Convenience method for setting a value to a path and notifying any - * elements bound to the same path. - * - * Note, if any part in the path except for the last is undefined, - * this method does nothing (this method does not throw when - * dereferencing undefined paths). - * - * @override - * @param {(string|!Array<(string|number)>)} path Path to the value - * to write. The path may be specified as a string (e.g. `'foo.bar.baz'`) - * or an array of path parts (e.g. `['foo.bar', 'baz']`). Note that - * bracketed expressions are not supported; string-based path parts - * *must* be separated by dots. Note that when dereferencing array - * indices, the index may be used as a dotted part directly - * (e.g. `'users.12.name'` or `['users', 12, 'name']`). - * @param {*} value Value to set at the specified path. - * @param {Object=} root Root object from which the path is evaluated. - * When specified, no notification will occur. - * @return {void} - * @public - */ - set(path, value, root) { - if (root) { - set(root, path, value); - } else { - if (!this[TYPES.READ_ONLY] || !this[TYPES.READ_ONLY][/** @type {string} */(path)]) { - if (this._setPendingPropertyOrPath(path, value, true)) { - this._invalidateProperties(); - } - } - } - } - - /** - * Adds items onto the end of the array at the path specified. - * - * The arguments after `path` and return value match that of - * `Array.prototype.push`. - * - * This method notifies other paths to the same array that a - * splice occurred to the array. - * - * @override - * @param {string | !Array<string|number>} path Path to array. - * @param {...*} items Items to push onto array - * @return {number} New length of the array. - * @public - */ - push(path, ...items) { - let info = {path: ''}; - let array = /** @type {Array}*/(get(this, path, info)); - let len = array.length; - let ret = array.push(...items); - if (items.length) { - notifySplice(this, array, info.path, len, items.length, []); - } - return ret; - } - - /** - * Removes an item from the end of array at the path specified. - * - * The arguments after `path` and return value match that of - * `Array.prototype.pop`. - * - * This method notifies other paths to the same array that a - * splice occurred to the array. - * - * @override - * @param {string | !Array<string|number>} path Path to array. - * @return {*} Item that was removed. - * @public - */ - pop(path) { - let info = {path: ''}; - let array = /** @type {Array} */(get(this, path, info)); - let hadLength = Boolean(array.length); - let ret = array.pop(); - if (hadLength) { - notifySplice(this, array, info.path, array.length, 0, [ret]); - } - return ret; - } - - /** - * Starting from the start index specified, removes 0 or more items - * from the array and inserts 0 or more new items in their place. - * - * The arguments after `path` and return value match that of - * `Array.prototype.splice`. - * - * This method notifies other paths to the same array that a - * splice occurred to the array. - * - * @override - * @param {string | !Array<string|number>} path Path to array. - * @param {number} start Index from which to start removing/inserting. - * @param {number=} deleteCount Number of items to remove. - * @param {...*} items Items to insert into array. - * @return {Array} Array of removed items. - * @public - */ - splice(path, start, deleteCount, ...items) { - let info = {path : ''}; - let array = /** @type {Array} */(get(this, path, info)); - // Normalize fancy native splice handling of crazy start values - if (start < 0) { - start = array.length - Math.floor(-start); - } else if (start) { - start = Math.floor(start); - } - // array.splice does different things based on the number of arguments - // you pass in. Therefore, array.splice(0) and array.splice(0, undefined) - // do different things. In the former, the whole array is cleared. In the - // latter, no items are removed. - // This means that we need to detect whether 1. one of the arguments - // is actually passed in and then 2. determine how many arguments - // we should pass on to the native array.splice - // - let ret; - // Omit any additional arguments if they were not passed in - if (arguments.length === 2) { - ret = array.splice(start); - // Either start was undefined and the others were defined, but in this - // case we can safely pass on all arguments - // - // Note: this includes the case where none of the arguments were passed in, - // e.g. this.splice('array'). However, if both start and deleteCount - // are undefined, array.splice will not modify the array (as expected) - } else { - ret = array.splice(start, deleteCount, ...items); - } - // At the end, check whether any items were passed in (e.g. insertions) - // or if the return array contains items (e.g. deletions). - // Only notify if items were added or deleted. - if (items.length || ret.length) { - notifySplice(this, array, info.path, start, items.length, ret); - } - return ret; - } - - /** - * Removes an item from the beginning of array at the path specified. - * - * The arguments after `path` and return value match that of - * `Array.prototype.pop`. - * - * This method notifies other paths to the same array that a - * splice occurred to the array. - * - * @override - * @param {string | !Array<string|number>} path Path to array. - * @return {*} Item that was removed. - * @public - */ - shift(path) { - let info = {path: ''}; - let array = /** @type {Array} */(get(this, path, info)); - let hadLength = Boolean(array.length); - let ret = array.shift(); - if (hadLength) { - notifySplice(this, array, info.path, 0, 0, [ret]); - } - return ret; - } - - /** - * Adds items onto the beginning of the array at the path specified. - * - * The arguments after `path` and return value match that of - * `Array.prototype.push`. - * - * This method notifies other paths to the same array that a - * splice occurred to the array. - * - * @override - * @param {string | !Array<string|number>} path Path to array. - * @param {...*} items Items to insert info array - * @return {number} New length of the array. - * @public - */ - unshift(path, ...items) { - let info = {path: ''}; - let array = /** @type {Array} */(get(this, path, info)); - let ret = array.unshift(...items); - if (items.length) { - notifySplice(this, array, info.path, 0, items.length, []); - } - return ret; - } - - /** - * Notify that a path has changed. - * - * Example: - * - * this.item.user.name = 'Bob'; - * this.notifyPath('item.user.name'); - * - * @override - * @param {string} path Path that should be notified. - * @param {*=} value Value at the path (optional). - * @return {void} - * @public - */ - notifyPath(path, value) { - /** @type {string} */ - let propPath; - if (arguments.length == 1) { - // Get value if not supplied - let info = {path: ''}; - value = get(this, path, info); - propPath = info.path; - } else if (Array.isArray(path)) { - // Normalize path if needed - propPath = normalize(path); - } else { - propPath = /** @type{string} */(path); - } - if (this._setPendingPropertyOrPath(propPath, value, true, true)) { - this._invalidateProperties(); - } - } - - /** - * Equivalent to static `createReadOnlyProperty` API but can be called on - * an instance to add effects at runtime. See that method for - * full API docs. - * - * @override - * @param {string} property Property name - * @param {boolean=} protectedSetter Creates a custom protected setter - * when `true`. - * @return {void} - * @protected - */ - _createReadOnlyProperty(property, protectedSetter) { - this._addPropertyEffect(property, TYPES.READ_ONLY); - if (protectedSetter) { - this['_set' + upper(property)] = /** @this {PropertyEffects} */function(value) { - this._setProperty(property, value); - }; - } - } - - /** - * Equivalent to static `createPropertyObserver` API but can be called on - * an instance to add effects at runtime. See that method for - * full API docs. - * - * @override - * @param {string} property Property name - * @param {string|function(*,*)} method Function or name of observer method - * to call - * @param {boolean=} dynamicFn Whether the method name should be included as - * a dependency to the effect. - * @return {void} - * @protected - */ - _createPropertyObserver(property, method, dynamicFn) { - let info = { property, method, dynamicFn: Boolean(dynamicFn) }; - this._addPropertyEffect(property, TYPES.OBSERVE, { - fn: runObserverEffect, info, trigger: {name: property} - }); - if (dynamicFn) { - this._addPropertyEffect(/** @type {string} */(method), TYPES.OBSERVE, { - fn: runObserverEffect, info, trigger: {name: method} - }); - } - } - - /** - * Equivalent to static `createMethodObserver` API but can be called on - * an instance to add effects at runtime. See that method for - * full API docs. - * - * @override - * @param {string} expression Method expression - * @param {boolean|Object=} dynamicFn Boolean or object map indicating - * whether method names should be included as a dependency to the effect. - * @return {void} - * @protected - */ - _createMethodObserver(expression, dynamicFn) { - let sig = parseMethod(expression); - if (!sig) { - throw new Error("Malformed observer expression '" + expression + "'"); - } - createMethodEffect(this, sig, TYPES.OBSERVE, runMethodEffect, null, dynamicFn); - } - - /** - * Equivalent to static `createNotifyingProperty` API but can be called on - * an instance to add effects at runtime. See that method for - * full API docs. - * - * @override - * @param {string} property Property name - * @return {void} - * @protected - */ - _createNotifyingProperty(property) { - this._addPropertyEffect(property, TYPES.NOTIFY, { - fn: runNotifyEffect, - info: { - eventName: camelToDashCase(property) + '-changed', - property: property - } - }); - } - - /** - * Equivalent to static `createReflectedProperty` API but can be called on - * an instance to add effects at runtime. See that method for - * full API docs. - * - * @override - * @param {string} property Property name - * @return {void} - * @protected - * @suppress {missingProperties} go/missingfnprops - */ - _createReflectedProperty(property) { - let attr = this.constructor.attributeNameForProperty(property); - if (attr[0] === '-') { - console.warn('Property ' + property + ' cannot be reflected to attribute ' + - attr + ' because "-" is not a valid starting attribute name. Use a lowercase first letter for the property instead.'); - } else { - this._addPropertyEffect(property, TYPES.REFLECT, { - fn: runReflectEffect, - info: { - attrName: attr - } - }); - } - } - - /** - * Equivalent to static `createComputedProperty` API but can be called on - * an instance to add effects at runtime. See that method for - * full API docs. - * - * @override - * @param {string} property Name of computed property to set - * @param {string} expression Method expression - * @param {boolean|Object=} dynamicFn Boolean or object map indicating - * whether method names should be included as a dependency to the effect. - * @return {void} - * @protected - */ - _createComputedProperty(property, expression, dynamicFn) { - let sig = parseMethod(expression); - if (!sig) { - throw new Error("Malformed computed expression '" + expression + "'"); - } - createMethodEffect(this, sig, TYPES.COMPUTE, runComputedEffect, property, dynamicFn); - } - - /** - * Gather the argument values for a method specified in the provided array - * of argument metadata. - * - * The `path` and `value` arguments are used to fill in wildcard descriptor - * when the method is being called as a result of a path notification. - * - * @param {!Array<!MethodArg>} args Array of argument metadata - * @param {string} path Property/path name that triggered the method effect - * @param {Object} props Bag of current property changes - * @return {Array<*>} Array of argument values - * @private - */ - _marshalArgs(args, path, props) { - const data = this.__data; - const values = []; - for (let i=0, l=args.length; i<l; i++) { - let {name, structured, wildcard, value, literal} = args[i]; - if (!literal) { - if (wildcard) { - const matches = isDescendant(name, path); - const pathValue = getArgValue(data, props, matches ? path : name); - value = { - path: matches ? path : name, - value: pathValue, - base: matches ? get(data, name) : pathValue - }; - } else { - value = structured ? getArgValue(data, props, name) : data[name]; - } - } - values[i] = value; - } - return values; - } - - // -- static class methods ------------ - - /** - * Ensures an accessor exists for the specified property, and adds - * to a list of "property effects" that will run when the accessor for - * the specified property is set. Effects are grouped by "type", which - * roughly corresponds to a phase in effect processing. The effect - * metadata should be in the following form: - * - * { - * fn: effectFunction, // Reference to function to call to perform effect - * info: { ... } // Effect metadata passed to function - * trigger: { // Optional triggering metadata; if not provided - * name: string // the property is treated as a wildcard - * structured: boolean - * wildcard: boolean - * } - * } - * - * Effects are called from `_propertiesChanged` in the following order by - * type: - * - * 1. COMPUTE - * 2. PROPAGATE - * 3. REFLECT - * 4. OBSERVE - * 5. NOTIFY - * - * Effect functions are called with the following signature: - * - * effectFunction(inst, path, props, oldProps, info, hasPaths) - * - * @param {string} property Property that should trigger the effect - * @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES - * @param {Object=} effect Effect metadata object - * @return {void} - * @protected - */ - static addPropertyEffect(property, type, effect) { - this.prototype._addPropertyEffect(property, type, effect); - } - - /** - * Creates a single-property observer for the given property. - * - * @param {string} property Property name - * @param {string|function(*,*)} method Function or name of observer method to call - * @param {boolean=} dynamicFn Whether the method name should be included as - * a dependency to the effect. - * @return {void} - * @protected - */ - static createPropertyObserver(property, method, dynamicFn) { - this.prototype._createPropertyObserver(property, method, dynamicFn); - } - - /** - * Creates a multi-property "method observer" based on the provided - * expression, which should be a string in the form of a normal JavaScript - * function signature: `'methodName(arg1, [..., argn])'`. Each argument - * should correspond to a property or path in the context of this - * prototype (or instance), or may be a literal string or number. - * - * @param {string} expression Method expression - * @param {boolean|Object=} dynamicFn Boolean or object map indicating - * @return {void} - * whether method names should be included as a dependency to the effect. - * @protected - */ - static createMethodObserver(expression, dynamicFn) { - this.prototype._createMethodObserver(expression, dynamicFn); - } - - /** - * Causes the setter for the given property to dispatch `<property>-changed` - * events to notify of changes to the property. - * - * @param {string} property Property name - * @return {void} - * @protected - */ - static createNotifyingProperty(property) { - this.prototype._createNotifyingProperty(property); - } - - /** - * Creates a read-only accessor for the given property. - * - * To set the property, use the protected `_setProperty` API. - * To create a custom protected setter (e.g. `_setMyProp()` for - * property `myProp`), pass `true` for `protectedSetter`. - * - * Note, if the property will have other property effects, this method - * should be called first, before adding other effects. - * - * @param {string} property Property name - * @param {boolean=} protectedSetter Creates a custom protected setter - * when `true`. - * @return {void} - * @protected - */ - static createReadOnlyProperty(property, protectedSetter) { - this.prototype._createReadOnlyProperty(property, protectedSetter); - } - - /** - * Causes the setter for the given property to reflect the property value - * to a (dash-cased) attribute of the same name. - * - * @param {string} property Property name - * @return {void} - * @protected - */ - static createReflectedProperty(property) { - this.prototype._createReflectedProperty(property); - } - - /** - * Creates a computed property whose value is set to the result of the - * method described by the given `expression` each time one or more - * arguments to the method changes. The expression should be a string - * in the form of a normal JavaScript function signature: - * `'methodName(arg1, [..., argn])'` - * - * @param {string} property Name of computed property to set - * @param {string} expression Method expression - * @param {boolean|Object=} dynamicFn Boolean or object map indicating whether - * method names should be included as a dependency to the effect. - * @return {void} - * @protected - */ - static createComputedProperty(property, expression, dynamicFn) { - this.prototype._createComputedProperty(property, expression, dynamicFn); - } - - /** - * Parses the provided template to ensure binding effects are created - * for them, and then ensures property accessors are created for any - * dependent properties in the template. Binding effects for bound - * templates are stored in a linked list on the instance so that - * templates can be efficiently stamped and unstamped. - * - * @param {!HTMLTemplateElement} template Template containing binding - * bindings - * @return {!TemplateInfo} Template metadata object - * @protected - */ - static bindTemplate(template) { - return this.prototype._bindTemplate(template); - } - - // -- binding ---------------------------------------------- - - /** - * Equivalent to static `bindTemplate` API but can be called on - * an instance to add effects at runtime. See that method for - * full API docs. - * - * This method may be called on the prototype (for prototypical template - * binding, to avoid creating accessors every instance) once per prototype, - * and will be called with `runtimeBinding: true` by `_stampTemplate` to - * create and link an instance of the template metadata associated with a - * particular stamping. - * - * @override - * @param {!HTMLTemplateElement} template Template containing binding - * bindings - * @param {boolean=} instanceBinding When false (default), performs - * "prototypical" binding of the template and overwrites any previously - * bound template for the class. When true (as passed from - * `_stampTemplate`), the template info is instanced and linked into - * the list of bound templates. - * @return {!TemplateInfo} Template metadata object; for `runtimeBinding`, - * this is an instance of the prototypical template info - * @protected - * @suppress {missingProperties} go/missingfnprops - */ - _bindTemplate(template, instanceBinding) { - let templateInfo = this.constructor._parseTemplate(template); - let wasPreBound = this.__templateInfo == templateInfo; - // Optimization: since this is called twice for proto-bound templates, - // don't attempt to recreate accessors if this template was pre-bound - if (!wasPreBound) { - for (let prop in templateInfo.propertyEffects) { - this._createPropertyAccessor(prop); - } - } - if (instanceBinding) { - // For instance-time binding, create instance of template metadata - // and link into list of templates if necessary - templateInfo = /** @type {!TemplateInfo} */(Object.create(templateInfo)); - templateInfo.wasPreBound = wasPreBound; - if (!wasPreBound && this.__templateInfo) { - let last = this.__templateInfoLast || this.__templateInfo; - this.__templateInfoLast = last.nextTemplateInfo = templateInfo; - templateInfo.previousTemplateInfo = last; - return templateInfo; - } - } - return this.__templateInfo = templateInfo; - } - - /** - * Adds a property effect to the given template metadata, which is run - * at the "propagate" stage of `_propertiesChanged` when the template - * has been bound to the element via `_bindTemplate`. - * - * The `effect` object should match the format in `_addPropertyEffect`. - * - * @param {Object} templateInfo Template metadata to add effect to - * @param {string} prop Property that should trigger the effect - * @param {Object=} effect Effect metadata object - * @return {void} - * @protected - */ - static _addTemplatePropertyEffect(templateInfo, prop, effect) { - let hostProps = templateInfo.hostProps = templateInfo.hostProps || {}; - hostProps[prop] = true; - let effects = templateInfo.propertyEffects = templateInfo.propertyEffects || {}; - let propEffects = effects[prop] = effects[prop] || []; - propEffects.push(effect); - } - - /** - * Stamps the provided template and performs instance-time setup for - * Polymer template features, including data bindings, declarative event - * listeners, and the `this.$` map of `id`'s to nodes. A document fragment - * is returned containing the stamped DOM, ready for insertion into the - * DOM. - * - * This method may be called more than once; however note that due to - * `shadycss` polyfill limitations, only styles from templates prepared - * using `ShadyCSS.prepareTemplate` will be correctly polyfilled (scoped - * to the shadow root and support CSS custom properties), and note that - * `ShadyCSS.prepareTemplate` may only be called once per element. As such, - * any styles required by in runtime-stamped templates must be included - * in the main element template. - * - * @param {!HTMLTemplateElement} template Template to stamp - * @return {!StampedTemplate} Cloned template content - * @override - * @protected - */ - _stampTemplate(template) { - // Ensures that created dom is `_enqueueClient`'d to this element so - // that it can be flushed on next call to `_flushProperties` - hostStack.beginHosting(this); - let dom = super._stampTemplate(template); - hostStack.endHosting(this); - let templateInfo = /** @type {!TemplateInfo} */(this._bindTemplate(template, true)); - // Add template-instance-specific data to instanced templateInfo - templateInfo.nodeList = dom.nodeList; - // Capture child nodes to allow unstamping of non-prototypical templates - if (!templateInfo.wasPreBound) { - let nodes = templateInfo.childNodes = []; - for (let n=dom.firstChild; n; n=n.nextSibling) { - nodes.push(n); - } - } - dom.templateInfo = templateInfo; - // Setup compound storage, 2-way listeners, and dataHost for bindings - setupBindings(this, templateInfo); - // Flush properties into template nodes if already booted - if (this.__dataReady) { - runEffects(this, templateInfo.propertyEffects, this.__data, null, - false, templateInfo.nodeList); - } - return dom; - } - - /** - * Removes and unbinds the nodes previously contained in the provided - * DocumentFragment returned from `_stampTemplate`. - * - * @override - * @param {!StampedTemplate} dom DocumentFragment previously returned - * from `_stampTemplate` associated with the nodes to be removed - * @return {void} - * @protected - */ - _removeBoundDom(dom) { - // Unlink template info - let templateInfo = dom.templateInfo; - if (templateInfo.previousTemplateInfo) { - templateInfo.previousTemplateInfo.nextTemplateInfo = - templateInfo.nextTemplateInfo; - } - if (templateInfo.nextTemplateInfo) { - templateInfo.nextTemplateInfo.previousTemplateInfo = - templateInfo.previousTemplateInfo; - } - if (this.__templateInfoLast == templateInfo) { - this.__templateInfoLast = templateInfo.previousTemplateInfo; - } - templateInfo.previousTemplateInfo = templateInfo.nextTemplateInfo = null; - // Remove stamped nodes - let nodes = templateInfo.childNodes; - for (let i=0; i<nodes.length; i++) { - let node = nodes[i]; - node.parentNode.removeChild(node); - } - } - - /** - * Overrides default `TemplateStamp` implementation to add support for - * parsing bindings from `TextNode`'s' `textContent`. A `bindings` - * array is added to `nodeInfo` and populated with binding metadata - * with information capturing the binding target, and a `parts` array - * with one or more metadata objects capturing the source(s) of the - * binding. - * - * @param {Node} node Node to parse - * @param {TemplateInfo} templateInfo Template metadata for current template - * @param {NodeInfo} nodeInfo Node metadata for current template node - * @return {boolean} `true` if the visited node added node-specific - * metadata to `nodeInfo` - * @protected - * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do - */ - static _parseTemplateNode(node, templateInfo, nodeInfo) { - let noted = super._parseTemplateNode(node, templateInfo, nodeInfo); - if (node.nodeType === Node.TEXT_NODE) { - let parts = this._parseBindings(node.textContent, templateInfo); - if (parts) { - // Initialize the textContent with any literal parts - // NOTE: default to a space here so the textNode remains; some browsers - // (IE) omit an empty textNode following cloneNode/importNode. - node.textContent = literalFromParts(parts) || ' '; - addBinding(this, templateInfo, nodeInfo, 'text', 'textContent', parts); - noted = true; - } - } - return noted; - } - - /** - * Overrides default `TemplateStamp` implementation to add support for - * parsing bindings from attributes. A `bindings` - * array is added to `nodeInfo` and populated with binding metadata - * with information capturing the binding target, and a `parts` array - * with one or more metadata objects capturing the source(s) of the - * binding. - * - * @param {Element} node Node to parse - * @param {TemplateInfo} templateInfo Template metadata for current template - * @param {NodeInfo} nodeInfo Node metadata for current template node - * @param {string} name Attribute name - * @param {string} value Attribute value - * @return {boolean} `true` if the visited node added node-specific - * metadata to `nodeInfo` - * @protected - * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do - */ - static _parseTemplateNodeAttribute(node, templateInfo, nodeInfo, name, value) { - let parts = this._parseBindings(value, templateInfo); - if (parts) { - // Attribute or property - let origName = name; - let kind = 'property'; - // The only way we see a capital letter here is if the attr has - // a capital letter in it per spec. In this case, to make sure - // this binding works, we go ahead and make the binding to the attribute. - if (capitalAttributeRegex.test(name)) { - kind = 'attribute'; - } else if (name[name.length-1] == '$') { - name = name.slice(0, -1); - kind = 'attribute'; - } - // Initialize attribute bindings with any literal parts - let literal = literalFromParts(parts); - if (literal && kind == 'attribute') { - // Ensure a ShadyCSS template scoped style is not removed - // when a class$ binding's initial literal value is set. - if (name == 'class' && node.hasAttribute('class')) { - literal += ' ' + node.getAttribute(name); - } - node.setAttribute(name, literal); - } - // Clear attribute before removing, since IE won't allow removing - // `value` attribute if it previously had a value (can't - // unconditionally set '' before removing since attributes with `$` - // can't be set using setAttribute) - if (node.localName === 'input' && origName === 'value') { - node.setAttribute(origName, ''); - } - // Remove annotation - node.removeAttribute(origName); - // Case hackery: attributes are lower-case, but bind targets - // (properties) are case sensitive. Gambit is to map dash-case to - // camel-case: `foo-bar` becomes `fooBar`. - // Attribute bindings are excepted. - if (kind === 'property') { - name = dashToCamelCase(name); - } - addBinding(this, templateInfo, nodeInfo, kind, name, parts, literal); - return true; - } else { - return super._parseTemplateNodeAttribute(node, templateInfo, nodeInfo, name, value); - } - } - - /** - * Overrides default `TemplateStamp` implementation to add support for - * binding the properties that a nested template depends on to the template - * as `_host_<property>`. - * - * @param {Node} node Node to parse - * @param {TemplateInfo} templateInfo Template metadata for current template - * @param {NodeInfo} nodeInfo Node metadata for current template node - * @return {boolean} `true` if the visited node added node-specific - * metadata to `nodeInfo` - * @protected - * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do - */ - static _parseTemplateNestedTemplate(node, templateInfo, nodeInfo) { - let noted = super._parseTemplateNestedTemplate(node, templateInfo, nodeInfo); - // Merge host props into outer template and add bindings - let hostProps = nodeInfo.templateInfo.hostProps; - let mode = '{'; - for (let source in hostProps) { - let parts = [{ mode, source, dependencies: [source] }]; - addBinding(this, templateInfo, nodeInfo, 'property', '_host_' + source, parts); - } - return noted; - } - - /** - * Called to parse text in a template (either attribute values or - * textContent) into binding metadata. - * - * Any overrides of this method should return an array of binding part - * metadata representing one or more bindings found in the provided text - * and any "literal" text in between. Any non-literal parts will be passed - * to `_evaluateBinding` when any dependencies change. The only required - * fields of each "part" in the returned array are as follows: - * - * - `dependencies` - Array containing trigger metadata for each property - * that should trigger the binding to update - * - `literal` - String containing text if the part represents a literal; - * in this case no `dependencies` are needed - * - * Additional metadata for use by `_evaluateBinding` may be provided in - * each part object as needed. - * - * The default implementation handles the following types of bindings - * (one or more may be intermixed with literal strings): - * - Property binding: `[[prop]]` - * - Path binding: `[[object.prop]]` - * - Negated property or path bindings: `[[!prop]]` or `[[!object.prop]]` - * - Two-way property or path bindings (supports negation): - * `{{prop}}`, `{{object.prop}}`, `{{!prop}}` or `{{!object.prop}}` - * - Inline computed method (supports negation): - * `[[compute(a, 'literal', b)]]`, `[[!compute(a, 'literal', b)]]` - * - * The default implementation uses a regular expression for best - * performance. However, the regular expression uses a white-list of - * allowed characters in a data-binding, which causes problems for - * data-bindings that do use characters not in this white-list. - * - * Instead of updating the white-list with all allowed characters, - * there is a StrictBindingParser (see lib/mixins/strict-binding-parser) - * that uses a state machine instead. This state machine is able to handle - * all characters. However, it is slightly less performant, therefore we - * extracted it into a separate optional mixin. - * - * @param {string} text Text to parse from attribute or textContent - * @param {Object} templateInfo Current template metadata - * @return {Array<!BindingPart>} Array of binding part metadata - * @protected - */ - static _parseBindings(text, templateInfo) { - let parts = []; - let lastIndex = 0; - let m; - // Example: "literal1{{prop}}literal2[[!compute(foo,bar)]]final" - // Regex matches: - // Iteration 1: Iteration 2: - // m[1]: '{{' '[[' - // m[2]: '' '!' - // m[3]: 'prop' 'compute(foo,bar)' - while ((m = bindingRegex.exec(text)) !== null) { - // Add literal part - if (m.index > lastIndex) { - parts.push({literal: text.slice(lastIndex, m.index)}); - } - // Add binding part - let mode = m[1][0]; - let negate = Boolean(m[2]); - let source = m[3].trim(); - let customEvent = false, notifyEvent = '', colon = -1; - if (mode == '{' && (colon = source.indexOf('::')) > 0) { - notifyEvent = source.substring(colon + 2); - source = source.substring(0, colon); - customEvent = true; - } - let signature = parseMethod(source); - let dependencies = []; - if (signature) { - // Inline computed function - let {args, methodName} = signature; - for (let i=0; i<args.length; i++) { - let arg = args[i]; - if (!arg.literal) { - dependencies.push(arg); - } - } - let dynamicFns = templateInfo.dynamicFns; - if (dynamicFns && dynamicFns[methodName] || signature.static) { - dependencies.push(methodName); - signature.dynamicFn = true; - } - } else { - // Property or path - dependencies.push(source); - } - parts.push({ - source, mode, negate, customEvent, signature, dependencies, - event: notifyEvent - }); - lastIndex = bindingRegex.lastIndex; - } - // Add a final literal part - if (lastIndex && lastIndex < text.length) { - let literal = text.substring(lastIndex); - if (literal) { - parts.push({ - literal: literal - }); - } - } - if (parts.length) { - return parts; - } else { - return null; - } - } - - /** - * Called to evaluate a previously parsed binding part based on a set of - * one or more changed dependencies. - * - * @param {!Polymer_PropertyEffects} inst Element that should be used as - * scope for binding dependencies - * @param {BindingPart} part Binding part metadata - * @param {string} path Property/path that triggered this effect - * @param {Object} props Bag of current property changes - * @param {Object} oldProps Bag of previous values for changed properties - * @param {boolean} hasPaths True with `props` contains one or more paths - * @return {*} Value the binding part evaluated to - * @protected - */ - static _evaluateBinding(inst, part, path, props, oldProps, hasPaths) { - let value; - if (part.signature) { - value = runMethodEffect(inst, path, props, oldProps, part.signature); - } else if (path != part.source) { - value = get(inst, part.source); - } else { - if (hasPaths && isPath(path)) { - value = get(inst, path); - } else { - value = inst.__data[path]; - } - } - if (part.negate) { - value = !value; - } - return value; - } - - } - - return PropertyEffects; -}); - -/** - * Helper api for enqueuing client dom created by a host element. - * - * By default elements are flushed via `_flushProperties` when - * `connectedCallback` is called. Elements attach their client dom to - * themselves at `ready` time which results from this first flush. - * This provides an ordering guarantee that the client dom an element - * creates is flushed before the element itself (i.e. client `ready` - * fires before host `ready`). - * - * However, if `_flushProperties` is called *before* an element is connected, - * as for example `Templatize` does, this ordering guarantee cannot be - * satisfied because no elements are connected. (Note: Bound elements that - * receive data do become enqueued clients and are properly ordered but - * unbound elements are not.) - * - * To maintain the desired "client before host" ordering guarantee for this - * case we rely on the "host stack. Client nodes registers themselves with - * the creating host element when created. This ensures that all client dom - * is readied in the proper order, maintaining the desired guarantee. - * - * @private - */ -class HostStack { - constructor() { - this.stack = []; - } - - /** - * @param {*} inst Instance to add to hostStack - * @return {void} - */ - registerHost(inst) { - if (this.stack.length) { - let host = this.stack[this.stack.length-1]; - host._enqueueClient(inst); - } - } - - /** - * @param {*} inst Instance to begin hosting - * @return {void} - */ - beginHosting(inst) { - this.stack.push(inst); - } - - /** - * @param {*} inst Instance to end hosting - * @return {void} - */ - endHosting(inst) { - let stackLen = this.stack.length; - if (stackLen && this.stack[stackLen-1] == inst) { - this.stack.pop(); - } - } -} -const hostStack = new HostStack();
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/strict-binding-parser.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/strict-binding-parser.js deleted file mode 100644 index 924c69c7..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/strict-binding-parser.js +++ /dev/null
@@ -1,414 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import '../utils/boot.js'; - -import { isPath } from '../utils/path.js'; -import { dedupingMixin } from '../utils/mixin.js'; -import { PropertyEffects } from './property-effects.js'; - -/** - * The open and corresponding closing brackets for surrounding bindings. - * @enum {string} - */ -const BINDINGS = { - '{': '}', - '[': ']' -}; - -/** - * All states that the parser can be in. The states represent the state-machine as a whole. - * @enum {number} - */ -const STATE = { - INITIAL: 1, - FIRSTOPENINGBINDING: 2, - FIRSTCHARACTERBINDING: 3, - BINDING: 4, - FIRSTCOLON: 5, - COLONNOTIFYEVENT: 6, - COLONNOTIFYEVENTFIRSTCLOSINGBINDING: 7, - FIRSTCLOSINGBINDING: 8, - STRING: 9, - METHOD: 10, - STRINGARG: 11, - NUMBERARG: 12, - VARIABLEARG: 13, - METHODCLOSED: 14, - METHODCLOSEDBINDING: 15 -}; - -function pushLiteral(text, i, parts, startChar) { - const literal = text.substring(startChar || 0, i); - if (literal) { - parts.push({ - literal - }); - } -} - -function storeMethod(bindingData, templateInfo) { - const methodName = bindingData.signature.methodName; - const dynamicFns = templateInfo.dynamicFns; - if (dynamicFns && dynamicFns[methodName] || bindingData.signature.static) { - bindingData.dependencies.push(methodName); - bindingData.signature.dynamicFn = true; - } -} - -function storeVariableBinding(parts, bindingData, prop, i) { - bindingData.source = prop; - bindingData.dependencies.push(prop); - bindingData.startChar = i + 1; - parts.push(bindingData); -} - -function storeMethodVariable(bindingData, text, i) { - const name = text.substring(bindingData.startChar, i).trim(); - if (name) { - if (name === 'true' || name === 'false') { - bindingData.signature.args.push({ - name, - value: name == 'true', - literal: true - }); - } else { - const arg = { - name - }; - arg.structured = isPath(name); - if (arg.structured) { - arg.wildcard = (name.slice(-2) == '.*'); - if (arg.wildcard) { - arg.name = name.slice(0, -2); - } - } - bindingData.signature.args.push(arg); - bindingData.dependencies.push(name); - bindingData.signature.static = false; - } - } -} - -function storeMethodNumber(bindingData, text, i) { - const value = text.substring(bindingData.startChar, i).trim(); - bindingData.signature.args.push({ - name: value, - value: Number(value), - literal: true - }); -} - -/** - * Mixin that parses binding expressions and generates corresponding metadata. - * The implementation is different than in `property-effects`, as it uses a - * state machine instead of a regex. As such, this implementation is able to - * handle more cases, with the potential performance hit. - * - * @mixinFunction - * @appliesMixin PropertyEffects - * @polymer - * @summary Mixin that parses binding expressions and generates corresponding metadata. - */ -const StrictBindingParser = dedupingMixin((base) => { - - /** - * @constructor - * @extends {base} - * @implements {Polymer_PropertyEffects} - * @private - */ - const elementBase = PropertyEffects(base); - - /** - * @polymer - * @mixinClass - * @implements {Polymer_PropertyEffects} - */ - return class extends elementBase { - - /** - * Called to parse text in a template (either attribute values or - * textContent) into binding metadata. - * - * Any overrides of this method should return an array of binding part - * metadata representing one or more bindings found in the provided text - * and any "literal" text in between. Any non-literal parts will be passed - * to `_evaluateBinding` when any dependencies change. The only required - * fields of each "part" in the returned array are as follows: - * - * - `dependencies` - Array containing trigger metadata for each property - * that should trigger the binding to update - * - `literal` - String containing text if the part represents a literal; - * in this case no `dependencies` are needed - * - * Additional metadata for use by `_evaluateBinding` may be provided in - * each part object as needed. - * - * The default implementation handles the following types of bindings - * (one or more may be intermixed with literal strings): - * - Property binding: `[[prop]]` - * - Path binding: `[[object.prop]]` - * - Negated property or path bindings: `[[!prop]]` or `[[!object.prop]]` - * - Two-way property or path bindings (supports negation): - * `{{prop}}`, `{{object.prop}}`, `{{!prop}}` or `{{!object.prop}}` - * - Inline computed method (supports negation): - * `[[compute(a, 'literal', b)]]`, `[[!compute(a, 'literal', b)]]` - * - * @param {string} text Text to parse from attribute or textContent - * @param {Object} templateInfo Current template metadata - * @return {Array<!BindingPart>} Array of binding part metadata - * @protected - */ - static _parseBindings(text, templateInfo) { - const parts = []; - let bindingData = {}; - let escaped = false; - /** @type {string} */ - let quote; - /** @type {number} */ - let state = STATE.INITIAL; - let i,l; - - for (i=0,l=text.length; i<l; i++) { - const char = text.charAt(i); - switch (state) { - case STATE.INITIAL: { - if ((char === '{' || char === '[')) { - bindingData = { - mode: char, - dependencies: [], - startChar: bindingData.startChar - }; - state = STATE.FIRSTOPENINGBINDING; - } - break; - } - case STATE.FIRSTOPENINGBINDING: { - if (char === bindingData.mode) { - pushLiteral(text, i - 1, parts, bindingData.startChar); - bindingData.startChar = i + 1; - state = STATE.FIRSTCHARACTERBINDING; - } else { - bindingData = {}; - state = STATE.INITIAL; - } - break; - } - case STATE.FIRSTCHARACTERBINDING: { - if (char !== ' ' && char !== '\t' && char !== '\n') { - if (char === '!') { - bindingData.negate = true; - bindingData.startChar = i + 1; - } - state = STATE.BINDING; - } - break; - } - case STATE.BINDING: { - switch (char) { - case BINDINGS[bindingData.mode]: { - state = STATE.FIRSTCLOSINGBINDING; - break; - } - case '\'': - case '"': { - quote = char; - state = STATE.STRING; - break; - } - case '(': { - bindingData.signature = { - methodName: text.substring(bindingData.startChar, i).trim(), - args: [], - static: true - }; - bindingData.startChar = i + 1; - state = STATE.METHOD; - break; - } - case ':': { - state = STATE.FIRSTCOLON; - } - } - break; - } - case STATE.FIRSTCOLON: { - if (char === ':') { - bindingData.customEvent = true; - bindingData.startCharAfterColon = i + 1; - state = STATE.COLONNOTIFYEVENT; - } else { - state = STATE.BINDING; - } - break; - } - case STATE.COLONNOTIFYEVENT: { - if (char === BINDINGS[bindingData.mode]) { - state = STATE.COLONNOTIFYEVENTFIRSTCLOSINGBINDING; - } - break; - } - case STATE.COLONNOTIFYEVENTFIRSTCLOSINGBINDING: { - if (char === BINDINGS[bindingData.mode]) { - bindingData.event = text.substring(bindingData.startCharAfterColon, i - 1).trim(); - const prop = text.substring(bindingData.startChar, bindingData.startCharAfterColon - 2).trim(); - storeVariableBinding(parts, bindingData, prop, i); - state = STATE.INITIAL; - } else { - state = STATE.BINDING; - } - break; - } - case STATE.FIRSTCLOSINGBINDING: { - if (char === BINDINGS[bindingData.mode]) { - const prop = text.substring(bindingData.startChar, i - 1).trim(); - storeVariableBinding(parts, bindingData, prop, i); - state = STATE.INITIAL; - } else { - state = STATE.BINDING; - } - break; - } - case STATE.STRING: { - if (char === '\\') { - escaped = true; - } else if (char === quote && !escaped) { - state = STATE.BINDING; - } else { - escaped = false; - } - break; - } - case STATE.METHOD: { - switch (char) { - case ')': { - storeMethodVariable(bindingData, text, i); - storeMethod(bindingData, templateInfo); - bindingData.startChar = i + 1; - state = STATE.METHODCLOSED; - break; - } - case ',': { - storeMethodVariable(bindingData, text, i); - bindingData.startChar = i + 1; - break; - } - case '\'': - case '"': { - quote = char; - state = STATE.STRINGARG; - break; - } - default: { - if (char >= '0' && char <= '9' || char === '-') { - state = STATE.NUMBERARG; - } else if (char != ' ' && char != '\n') { - state = STATE.VARIABLEARG; - } - } - } - break; - } - case STATE.STRINGARG: { - if (char === '\\') { - escaped = true; - } else if (char === quote && !escaped) { - const value = text.substring(bindingData.startChar, i) - .replace(/^\s+/, '') - .substring(1) - // replace comma entity with comma - .replace(/,/g, ',') - // repair extra escape sequences; note only commas strictly need - // escaping, but we allow any other char to be escaped since its - // likely users will do this - .replace(/\\(.)/g, '\$1'); - bindingData.signature.args.push({ - value, - name: value, - literal: true - }); - bindingData.startChar = i + 1; - state = STATE.METHOD; - } else { - escaped = false; - } - break; - } - case STATE.NUMBERARG: { - switch (char) { - case ',': { - storeMethodNumber(bindingData, text, i); - bindingData.startChar = i + 1; - state = STATE.METHOD; - break; - } - case ')': { - storeMethodNumber(bindingData, text, i); - storeMethod(bindingData, templateInfo); - state = STATE.METHODCLOSED; - break; - } - default: { - if (char < '0' || char > '9') { - state = STATE.VARIABLEARG; - } - } - } - break; - } - case STATE.VARIABLEARG: { - switch (char) { - case ',': { - storeMethodVariable(bindingData, text, i); - bindingData.startChar = i + 1; - state = STATE.METHOD; - break; - } - case ')': { - storeMethodVariable(bindingData, text, i); - storeMethod(bindingData, templateInfo); - state = STATE.METHODCLOSED; - break; - } - } - break; - } - case STATE.METHODCLOSED: { - if (char === BINDINGS[bindingData.mode]) { - state = STATE.METHODCLOSEDBINDING; - } else if (char !== ' ' && char !== '\t' && char !== '\n') { - console.warn(`Expected two closing "${BINDINGS[bindingData.mode]}" for binding "${text}"`); - } - break; - } - case STATE.METHODCLOSEDBINDING: { - if (char === BINDINGS[bindingData.mode]) { - bindingData.startChar = i + 1; - parts.push(bindingData); - state = STATE.INITIAL; - } else if (char !== ' ' && char !== '\t' && char !== '\n') { - console.warn(`Expected one closing "${BINDINGS[bindingData.mode]}" for binding "${text}"`); - } - break; - } - } - } - - if (parts.length) { - pushLiteral(text, i, parts, parts[parts.length - 1].startChar); - return parts; - } - - return null; - } - }; -}); - -export { StrictBindingParser };
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/template-stamp.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/template-stamp.js deleted file mode 100644 index 167fbe53..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/template-stamp.js +++ /dev/null
@@ -1,500 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import '../utils/boot.js'; - -import { dedupingMixin } from '../utils/mixin.js'; - -const walker = document.createTreeWalker(document, NodeFilter.SHOW_ALL, - null, false); - -// 1.x backwards-compatible auto-wrapper for template type extensions -// This is a clear layering violation and gives favored-nation status to -// dom-if and dom-repeat templates. This is a conceit we're choosing to keep -// a.) to ease 1.x backwards-compatibility due to loss of `is`, and -// b.) to maintain if/repeat capability in parser-constrained elements -// (e.g. table, select) in lieu of native CE type extensions without -// massive new invention in this space (e.g. directive system) -const templateExtensions = { - 'dom-if': true, - 'dom-repeat': true -}; -function wrapTemplateExtension(node) { - let is = node.getAttribute('is'); - if (is && templateExtensions[is]) { - let t = node; - t.removeAttribute('is'); - node = t.ownerDocument.createElement(is); - t.parentNode.replaceChild(node, t); - node.appendChild(t); - while(t.attributes.length) { - node.setAttribute(t.attributes[0].name, t.attributes[0].value); - t.removeAttribute(t.attributes[0].name); - } - } - return node; -} - -function findTemplateNode(root, nodeInfo) { - // recursively ascend tree until we hit root - let parent = nodeInfo.parentInfo && findTemplateNode(root, nodeInfo.parentInfo); - // unwind the stack, returning the indexed node at each level - if (parent) { - // note: marginally faster than indexing via childNodes - // (http://jsperf.com/childnodes-lookup) - walker.currentNode = parent; - for (let n=walker.firstChild(), i=0; n; n=walker.nextSibling()) { - if (nodeInfo.parentIndex === i++) { - return n; - } - } - } else { - return root; - } -} - -// construct `$` map (from id annotations) -function applyIdToMap(inst, map, node, nodeInfo) { - if (nodeInfo.id) { - map[nodeInfo.id] = node; - } -} - -// install event listeners (from event annotations) -function applyEventListener(inst, node, nodeInfo) { - if (nodeInfo.events && nodeInfo.events.length) { - for (let j=0, e$=nodeInfo.events, e; (j<e$.length) && (e=e$[j]); j++) { - inst._addMethodEventListenerToNode(node, e.name, e.value, inst); - } - } -} - -// push configuration references at configure time -function applyTemplateContent(inst, node, nodeInfo) { - if (nodeInfo.templateInfo) { - node._templateInfo = nodeInfo.templateInfo; - } -} - -function createNodeEventHandler(context, eventName, methodName) { - // Instances can optionally have a _methodHost which allows redirecting where - // to find methods. Currently used by `templatize`. - context = context._methodHost || context; - let handler = function(e) { - if (context[methodName]) { - context[methodName](e, e.detail); - } else { - console.warn('listener method `' + methodName + '` not defined'); - } - }; - return handler; -} - -/** - * Element mixin that provides basic template parsing and stamping, including - * the following template-related features for stamped templates: - * - * - Declarative event listeners (`on-eventname="listener"`) - * - Map of node id's to stamped node instances (`this.$.id`) - * - Nested template content caching/removal and re-installation (performance - * optimization) - * - * @mixinFunction - * @polymer - * @summary Element class mixin that provides basic template parsing and stamping - */ -export const TemplateStamp = dedupingMixin( - /** - * @template T - * @param {function(new:T)} superClass Class to apply mixin to. - * @return {function(new:T)} superClass with mixin applied. - */ - (superClass) => { - - /** - * @polymer - * @mixinClass - * @implements {Polymer_TemplateStamp} - */ - class TemplateStamp extends superClass { - - /** - * Scans a template to produce template metadata. - * - * Template-specific metadata are stored in the object returned, and node- - * specific metadata are stored in objects in its flattened `nodeInfoList` - * array. Only nodes in the template that were parsed as nodes of - * interest contain an object in `nodeInfoList`. Each `nodeInfo` object - * contains an `index` (`childNodes` index in parent) and optionally - * `parent`, which points to node info of its parent (including its index). - * - * The template metadata object returned from this method has the following - * structure (many fields optional): - * - * ```js - * { - * // Flattened list of node metadata (for nodes that generated metadata) - * nodeInfoList: [ - * { - * // `id` attribute for any nodes with id's for generating `$` map - * id: {string}, - * // `on-event="handler"` metadata - * events: [ - * { - * name: {string}, // event name - * value: {string}, // handler method name - * }, ... - * ], - * // Notes when the template contained a `<slot>` for shady DOM - * // optimization purposes - * hasInsertionPoint: {boolean}, - * // For nested `<template>`` nodes, nested template metadata - * templateInfo: {object}, // nested template metadata - * // Metadata to allow efficient retrieval of instanced node - * // corresponding to this metadata - * parentInfo: {number}, // reference to parent nodeInfo> - * parentIndex: {number}, // index in parent's `childNodes` collection - * infoIndex: {number}, // index of this `nodeInfo` in `templateInfo.nodeInfoList` - * }, - * ... - * ], - * // When true, the template had the `strip-whitespace` attribute - * // or was nested in a template with that setting - * stripWhitespace: {boolean}, - * // For nested templates, nested template content is moved into - * // a document fragment stored here; this is an optimization to - * // avoid the cost of nested template cloning - * content: {DocumentFragment} - * } - * ``` - * - * This method kicks off a recursive treewalk as follows: - * - * ``` - * _parseTemplate <---------------------+ - * _parseTemplateContent | - * _parseTemplateNode <------------|--+ - * _parseTemplateNestedTemplate --+ | - * _parseTemplateChildNodes ---------+ - * _parseTemplateNodeAttributes - * _parseTemplateNodeAttribute - * - * ``` - * - * These methods may be overridden to add custom metadata about templates - * to either `templateInfo` or `nodeInfo`. - * - * Note that this method may be destructive to the template, in that - * e.g. event annotations may be removed after being noted in the - * template metadata. - * - * @param {!HTMLTemplateElement} template Template to parse - * @param {TemplateInfo=} outerTemplateInfo Template metadata from the outer - * template, for parsing nested templates - * @return {!TemplateInfo} Parsed template metadata - */ - static _parseTemplate(template, outerTemplateInfo) { - // since a template may be re-used, memo-ize metadata - if (!template._templateInfo) { - let templateInfo = template._templateInfo = {}; - templateInfo.nodeInfoList = []; - templateInfo.stripWhiteSpace = - (outerTemplateInfo && outerTemplateInfo.stripWhiteSpace) || - template.hasAttribute('strip-whitespace'); - this._parseTemplateContent(template, templateInfo, {parent: null}); - } - return template._templateInfo; - } - - static _parseTemplateContent(template, templateInfo, nodeInfo) { - return this._parseTemplateNode(template.content, templateInfo, nodeInfo); - } - - /** - * Parses template node and adds template and node metadata based on - * the current node, and its `childNodes` and `attributes`. - * - * This method may be overridden to add custom node or template specific - * metadata based on this node. - * - * @param {Node} node Node to parse - * @param {!TemplateInfo} templateInfo Template metadata for current template - * @param {!NodeInfo} nodeInfo Node metadata for current template. - * @return {boolean} `true` if the visited node added node-specific - * metadata to `nodeInfo` - */ - static _parseTemplateNode(node, templateInfo, nodeInfo) { - let noted; - let element = /** @type {Element} */(node); - if (element.localName == 'template' && !element.hasAttribute('preserve-content')) { - noted = this._parseTemplateNestedTemplate(element, templateInfo, nodeInfo) || noted; - } else if (element.localName === 'slot') { - // For ShadyDom optimization, indicating there is an insertion point - templateInfo.hasInsertionPoint = true; - } - walker.currentNode = element; - if (walker.firstChild()) { - noted = this._parseTemplateChildNodes(element, templateInfo, nodeInfo) || noted; - } - if (element.hasAttributes && element.hasAttributes()) { - noted = this._parseTemplateNodeAttributes(element, templateInfo, nodeInfo) || noted; - } - return noted; - } - - /** - * Parses template child nodes for the given root node. - * - * This method also wraps whitelisted legacy template extensions - * (`is="dom-if"` and `is="dom-repeat"`) with their equivalent element - * wrappers, collapses text nodes, and strips whitespace from the template - * if the `templateInfo.stripWhitespace` setting was provided. - * - * @param {Node} root Root node whose `childNodes` will be parsed - * @param {!TemplateInfo} templateInfo Template metadata for current template - * @param {!NodeInfo} nodeInfo Node metadata for current template. - * @return {void} - */ - static _parseTemplateChildNodes(root, templateInfo, nodeInfo) { - if (root.localName === 'script' || root.localName === 'style') { - return; - } - walker.currentNode = root; - for (let node=walker.firstChild(), parentIndex=0, next; node; node=next) { - // Wrap templates - if (node.localName == 'template') { - node = wrapTemplateExtension(node); - } - // collapse adjacent textNodes: fixes an IE issue that can cause - // text nodes to be inexplicably split =( - // note that root.normalize() should work but does not so we do this - // manually. - walker.currentNode = node; - next = walker.nextSibling(); - if (node.nodeType === Node.TEXT_NODE) { - let /** Node */ n = next; - while (n && (n.nodeType === Node.TEXT_NODE)) { - node.textContent += n.textContent; - next = walker.nextSibling(); - root.removeChild(n); - n = next; - } - // optionally strip whitespace - if (templateInfo.stripWhiteSpace && !node.textContent.trim()) { - root.removeChild(node); - continue; - } - } - let childInfo = { parentIndex, parentInfo: nodeInfo }; - if (this._parseTemplateNode(node, templateInfo, childInfo)) { - childInfo.infoIndex = templateInfo.nodeInfoList.push(/** @type {!NodeInfo} */(childInfo)) - 1; - } - // Increment if not removed - walker.currentNode = node; - if (walker.parentNode()) { - parentIndex++; - } - } - } - - /** - * Parses template content for the given nested `<template>`. - * - * Nested template info is stored as `templateInfo` in the current node's - * `nodeInfo`. `template.content` is removed and stored in `templateInfo`. - * It will then be the responsibility of the host to set it back to the - * template and for users stamping nested templates to use the - * `_contentForTemplate` method to retrieve the content for this template - * (an optimization to avoid the cost of cloning nested template content). - * - * @param {HTMLTemplateElement} node Node to parse (a <template>) - * @param {TemplateInfo} outerTemplateInfo Template metadata for current template - * that includes the template `node` - * @param {!NodeInfo} nodeInfo Node metadata for current template. - * @return {boolean} `true` if the visited node added node-specific - * metadata to `nodeInfo` - */ - static _parseTemplateNestedTemplate(node, outerTemplateInfo, nodeInfo) { - let templateInfo = this._parseTemplate(node, outerTemplateInfo); - let content = templateInfo.content = - node.content.ownerDocument.createDocumentFragment(); - content.appendChild(node.content); - nodeInfo.templateInfo = templateInfo; - return true; - } - - /** - * Parses template node attributes and adds node metadata to `nodeInfo` - * for nodes of interest. - * - * @param {Element} node Node to parse - * @param {TemplateInfo} templateInfo Template metadata for current template - * @param {NodeInfo} nodeInfo Node metadata for current template. - * @return {boolean} `true` if the visited node added node-specific - * metadata to `nodeInfo` - */ - static _parseTemplateNodeAttributes(node, templateInfo, nodeInfo) { - // Make copy of original attribute list, since the order may change - // as attributes are added and removed - let noted = false; - let attrs = Array.from(node.attributes); - for (let i=attrs.length-1, a; (a=attrs[i]); i--) { - noted = this._parseTemplateNodeAttribute(node, templateInfo, nodeInfo, a.name, a.value) || noted; - } - return noted; - } - - /** - * Parses a single template node attribute and adds node metadata to - * `nodeInfo` for attributes of interest. - * - * This implementation adds metadata for `on-event="handler"` attributes - * and `id` attributes. - * - * @param {Element} node Node to parse - * @param {!TemplateInfo} templateInfo Template metadata for current template - * @param {!NodeInfo} nodeInfo Node metadata for current template. - * @param {string} name Attribute name - * @param {string} value Attribute value - * @return {boolean} `true` if the visited node added node-specific - * metadata to `nodeInfo` - */ - static _parseTemplateNodeAttribute(node, templateInfo, nodeInfo, name, value) { - // events (on-*) - if (name.slice(0, 3) === 'on-') { - node.removeAttribute(name); - nodeInfo.events = nodeInfo.events || []; - nodeInfo.events.push({ - name: name.slice(3), - value - }); - return true; - } - // static id - else if (name === 'id') { - nodeInfo.id = value; - return true; - } - return false; - } - - /** - * Returns the `content` document fragment for a given template. - * - * For nested templates, Polymer performs an optimization to cache nested - * template content to avoid the cost of cloning deeply nested templates. - * This method retrieves the cached content for a given template. - * - * @param {HTMLTemplateElement} template Template to retrieve `content` for - * @return {DocumentFragment} Content fragment - */ - static _contentForTemplate(template) { - let templateInfo = /** @type {HTMLTemplateElementWithInfo} */ (template)._templateInfo; - return (templateInfo && templateInfo.content) || template.content; - } - - /** - * Clones the provided template content and returns a document fragment - * containing the cloned dom. - * - * The template is parsed (once and memoized) using this library's - * template parsing features, and provides the following value-added - * features: - * * Adds declarative event listeners for `on-event="handler"` attributes - * * Generates an "id map" for all nodes with id's under `$` on returned - * document fragment - * * Passes template info including `content` back to templates as - * `_templateInfo` (a performance optimization to avoid deep template - * cloning) - * - * Note that the memoized template parsing process is destructive to the - * template: attributes for bindings and declarative event listeners are - * removed after being noted in notes, and any nested `<template>.content` - * is removed and stored in notes as well. - * - * @param {!HTMLTemplateElement} template Template to stamp - * @return {!StampedTemplate} Cloned template content - * @override - */ - _stampTemplate(template) { - // Polyfill support: bootstrap the template if it has not already been - if (template && !template.content && - window.HTMLTemplateElement && HTMLTemplateElement.decorate) { - HTMLTemplateElement.decorate(template); - } - let templateInfo = this.constructor._parseTemplate(template); - let nodeInfo = templateInfo.nodeInfoList; - let content = templateInfo.content || template.content; - let dom = /** @type {DocumentFragment} */ (document.importNode(content, true)); - // NOTE: ShadyDom optimization indicating there is an insertion point - dom.__noInsertionPoint = !templateInfo.hasInsertionPoint; - let nodes = dom.nodeList = new Array(nodeInfo.length); - dom.$ = {}; - for (let i=0, l=nodeInfo.length, info; (i<l) && (info=nodeInfo[i]); i++) { - let node = nodes[i] = findTemplateNode(dom, info); - applyIdToMap(this, dom.$, node, info); - applyTemplateContent(this, node, info); - applyEventListener(this, node, info); - } - dom = /** @type {!StampedTemplate} */(dom); // eslint-disable-line no-self-assign - return dom; - } - - /** - * Adds an event listener by method name for the event provided. - * - * This method generates a handler function that looks up the method - * name at handling time. - * - * @param {!EventTarget} node Node to add listener on - * @param {string} eventName Name of event - * @param {string} methodName Name of method - * @param {*=} context Context the method will be called on (defaults - * to `node`) - * @return {Function} Generated handler function - * @override - */ - _addMethodEventListenerToNode(node, eventName, methodName, context) { - context = context || node; - let handler = createNodeEventHandler(context, eventName, methodName); - this._addEventListenerToNode(node, eventName, handler); - return handler; - } - - /** - * Override point for adding custom or simulated event handling. - * - * @param {!EventTarget} node Node to add event listener to - * @param {string} eventName Name of event - * @param {function(!Event):void} handler Listener function to add - * @return {void} - * @override - */ - _addEventListenerToNode(node, eventName, handler) { - node.addEventListener(eventName, handler); - } - - /** - * Override point for adding custom or simulated event handling. - * - * @param {!EventTarget} node Node to remove event listener from - * @param {string} eventName Name of event - * @param {function(!Event):void} handler Listener function to remove - * @return {void} - * @override - */ - _removeEventListenerFromNode(node, eventName, handler) { - node.removeEventListener(eventName, handler); - } - - } - - return TemplateStamp; - -});
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/array-splice.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/array-splice.js deleted file mode 100644 index 77ccb1a..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/array-splice.js +++ /dev/null
@@ -1,299 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import './boot.js'; - -function newSplice(index, removed, addedCount) { - return { - index: index, - removed: removed, - addedCount: addedCount - }; -} - -const EDIT_LEAVE = 0; -const EDIT_UPDATE = 1; -const EDIT_ADD = 2; -const EDIT_DELETE = 3; - -// Note: This function is *based* on the computation of the Levenshtein -// "edit" distance. The one change is that "updates" are treated as two -// edits - not one. With Array splices, an update is really a delete -// followed by an add. By retaining this, we optimize for "keeping" the -// maximum array items in the original array. For example: -// -// 'xxxx123' -> '123yyyy' -// -// With 1-edit updates, the shortest path would be just to update all seven -// characters. With 2-edit updates, we delete 4, leave 3, and add 4. This -// leaves the substring '123' intact. -function calcEditDistances(current, currentStart, currentEnd, - old, oldStart, oldEnd) { - // "Deletion" columns - let rowCount = oldEnd - oldStart + 1; - let columnCount = currentEnd - currentStart + 1; - let distances = new Array(rowCount); - - // "Addition" rows. Initialize null column. - for (let i = 0; i < rowCount; i++) { - distances[i] = new Array(columnCount); - distances[i][0] = i; - } - - // Initialize null row - for (let j = 0; j < columnCount; j++) - distances[0][j] = j; - - for (let i = 1; i < rowCount; i++) { - for (let j = 1; j < columnCount; j++) { - if (equals(current[currentStart + j - 1], old[oldStart + i - 1])) - distances[i][j] = distances[i - 1][j - 1]; - else { - let north = distances[i - 1][j] + 1; - let west = distances[i][j - 1] + 1; - distances[i][j] = north < west ? north : west; - } - } - } - - return distances; -} - -// This starts at the final weight, and walks "backward" by finding -// the minimum previous weight recursively until the origin of the weight -// matrix. -function spliceOperationsFromEditDistances(distances) { - let i = distances.length - 1; - let j = distances[0].length - 1; - let current = distances[i][j]; - let edits = []; - while (i > 0 || j > 0) { - if (i == 0) { - edits.push(EDIT_ADD); - j--; - continue; - } - if (j == 0) { - edits.push(EDIT_DELETE); - i--; - continue; - } - let northWest = distances[i - 1][j - 1]; - let west = distances[i - 1][j]; - let north = distances[i][j - 1]; - - let min; - if (west < north) - min = west < northWest ? west : northWest; - else - min = north < northWest ? north : northWest; - - if (min == northWest) { - if (northWest == current) { - edits.push(EDIT_LEAVE); - } else { - edits.push(EDIT_UPDATE); - current = northWest; - } - i--; - j--; - } else if (min == west) { - edits.push(EDIT_DELETE); - i--; - current = west; - } else { - edits.push(EDIT_ADD); - j--; - current = north; - } - } - - edits.reverse(); - return edits; -} - -/** - * Splice Projection functions: - * - * A splice map is a representation of how a previous array of items - * was transformed into a new array of items. Conceptually it is a list of - * tuples of - * - * <index, removed, addedCount> - * - * which are kept in ascending index order of. The tuple represents that at - * the |index|, |removed| sequence of items were removed, and counting forward - * from |index|, |addedCount| items were added. - */ - -/** - * Lacking individual splice mutation information, the minimal set of - * splices can be synthesized given the previous state and final state of an - * array. The basic approach is to calculate the edit distance matrix and - * choose the shortest path through it. - * - * Complexity: O(l * p) - * l: The length of the current array - * p: The length of the old array - * - * @param {!Array} current The current "changed" array for which to - * calculate splices. - * @param {number} currentStart Starting index in the `current` array for - * which splices are calculated. - * @param {number} currentEnd Ending index in the `current` array for - * which splices are calculated. - * @param {!Array} old The original "unchanged" array to compare `current` - * against to determine splices. - * @param {number} oldStart Starting index in the `old` array for - * which splices are calculated. - * @param {number} oldEnd Ending index in the `old` array for - * which splices are calculated. - * @return {!Array} Returns an array of splice record objects. Each of these - * contains: `index` the location where the splice occurred; `removed` - * the array of removed items from this location; `addedCount` the number - * of items added at this location. - */ -function calcSplices(current, currentStart, currentEnd, - old, oldStart, oldEnd) { - let prefixCount = 0; - let suffixCount = 0; - let splice; - - let minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart); - if (currentStart == 0 && oldStart == 0) - prefixCount = sharedPrefix(current, old, minLength); - - if (currentEnd == current.length && oldEnd == old.length) - suffixCount = sharedSuffix(current, old, minLength - prefixCount); - - currentStart += prefixCount; - oldStart += prefixCount; - currentEnd -= suffixCount; - oldEnd -= suffixCount; - - if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0) - return []; - - if (currentStart == currentEnd) { - splice = newSplice(currentStart, [], 0); - while (oldStart < oldEnd) - splice.removed.push(old[oldStart++]); - - return [ splice ]; - } else if (oldStart == oldEnd) - return [ newSplice(currentStart, [], currentEnd - currentStart) ]; - - let ops = spliceOperationsFromEditDistances( - calcEditDistances(current, currentStart, currentEnd, - old, oldStart, oldEnd)); - - splice = undefined; - let splices = []; - let index = currentStart; - let oldIndex = oldStart; - for (let i = 0; i < ops.length; i++) { - switch(ops[i]) { - case EDIT_LEAVE: - if (splice) { - splices.push(splice); - splice = undefined; - } - - index++; - oldIndex++; - break; - case EDIT_UPDATE: - if (!splice) - splice = newSplice(index, [], 0); - - splice.addedCount++; - index++; - - splice.removed.push(old[oldIndex]); - oldIndex++; - break; - case EDIT_ADD: - if (!splice) - splice = newSplice(index, [], 0); - - splice.addedCount++; - index++; - break; - case EDIT_DELETE: - if (!splice) - splice = newSplice(index, [], 0); - - splice.removed.push(old[oldIndex]); - oldIndex++; - break; - } - } - - if (splice) { - splices.push(splice); - } - return splices; -} - -function sharedPrefix(current, old, searchLength) { - for (let i = 0; i < searchLength; i++) - if (!equals(current[i], old[i])) - return i; - return searchLength; -} - -function sharedSuffix(current, old, searchLength) { - let index1 = current.length; - let index2 = old.length; - let count = 0; - while (count < searchLength && equals(current[--index1], old[--index2])) - count++; - - return count; -} - -/** - * Returns an array of splice records indicating the minimum edits required - * to transform the `previous` array into the `current` array. - * - * Splice records are ordered by index and contain the following fields: - * - `index`: index where edit started - * - `removed`: array of removed items from this index - * - `addedCount`: number of items added at this index - * - * This function is based on the Levenshtein "minimum edit distance" - * algorithm. Note that updates are treated as removal followed by addition. - * - * The worst-case time complexity of this algorithm is `O(l * p)` - * l: The length of the current array - * p: The length of the previous array - * - * However, the worst-case complexity is reduced by an `O(n)` optimization - * to detect any shared prefix & suffix between the two arrays and only - * perform the more expensive minimum edit distance calculation over the - * non-shared portions of the arrays. - * - * @function - * @param {!Array} current The "changed" array for which splices will be - * calculated. - * @param {!Array} previous The "unchanged" original array to compare - * `current` against to determine the splices. - * @return {!Array} Returns an array of splice record objects. Each of these - * contains: `index` the location where the splice occurred; `removed` - * the array of removed items from this location; `addedCount` the number - * of items added at this location. - */ -export function calculateSplices(current, previous) { - return calcSplices(current, 0, current.length, previous, 0, - previous.length); -} - -function equals(currentValue, previousValue) { - return currentValue === previousValue; -}
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/async.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/async.js deleted file mode 100644 index e332f95..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/async.js +++ /dev/null
@@ -1,207 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -/** - * @fileoverview - * - * This module provides a number of strategies for enqueuing asynchronous - * tasks. Each sub-module provides a standard `run(fn)` interface that returns a - * handle, and a `cancel(handle)` interface for canceling async tasks before - * they run. - * - * @summary Module that provides a number of strategies for enqueuing - * asynchronous tasks. - */ - -import './boot.js'; - -// Microtask implemented using Mutation Observer -let microtaskCurrHandle = 0; -let microtaskLastHandle = 0; -let microtaskCallbacks = []; -let microtaskNodeContent = 0; -let microtaskNode = document.createTextNode(''); -new window.MutationObserver(microtaskFlush).observe(microtaskNode, {characterData: true}); - -function microtaskFlush() { - const len = microtaskCallbacks.length; - for (let i = 0; i < len; i++) { - let cb = microtaskCallbacks[i]; - if (cb) { - try { - cb(); - } catch (e) { - setTimeout(() => { throw e; }); - } - } - } - microtaskCallbacks.splice(0, len); - microtaskLastHandle += len; -} - -/** - * Async interface wrapper around `setTimeout`. - * - * @namespace - * @summary Async interface wrapper around `setTimeout`. - */ -const timeOut = { - /** - * Returns a sub-module with the async interface providing the provided - * delay. - * - * @memberof timeOut - * @param {number=} delay Time to wait before calling callbacks in ms - * @return {!AsyncInterface} An async timeout interface - */ - after(delay) { - return { - run(fn) { return window.setTimeout(fn, delay); }, - cancel(handle) { - window.clearTimeout(handle); - } - }; - }, - /** - * Enqueues a function called in the next task. - * - * @memberof timeOut - * @param {!Function} fn Callback to run - * @param {number=} delay Delay in milliseconds - * @return {number} Handle used for canceling task - */ - run(fn, delay) { - return window.setTimeout(fn, delay); - }, - /** - * Cancels a previously enqueued `timeOut` callback. - * - * @memberof timeOut - * @param {number} handle Handle returned from `run` of callback to cancel - * @return {void} - */ - cancel(handle) { - window.clearTimeout(handle); - } -}; -export {timeOut}; - -/** - * Async interface wrapper around `requestAnimationFrame`. - * - * @namespace - * @summary Async interface wrapper around `requestAnimationFrame`. - */ -const animationFrame = { - /** - * Enqueues a function called at `requestAnimationFrame` timing. - * - * @memberof animationFrame - * @param {function(number):void} fn Callback to run - * @return {number} Handle used for canceling task - */ - run(fn) { - return window.requestAnimationFrame(fn); - }, - /** - * Cancels a previously enqueued `animationFrame` callback. - * - * @memberof animationFrame - * @param {number} handle Handle returned from `run` of callback to cancel - * @return {void} - */ - cancel(handle) { - window.cancelAnimationFrame(handle); - } -}; -export {animationFrame}; - -/** - * Async interface wrapper around `requestIdleCallback`. Falls back to - * `setTimeout` on browsers that do not support `requestIdleCallback`. - * - * @namespace - * @summary Async interface wrapper around `requestIdleCallback`. - */ -const idlePeriod = { - /** - * Enqueues a function called at `requestIdleCallback` timing. - * - * @memberof idlePeriod - * @param {function(!IdleDeadline):void} fn Callback to run - * @return {number} Handle used for canceling task - */ - run(fn) { - return window.requestIdleCallback ? - window.requestIdleCallback(fn) : - window.setTimeout(fn, 16); - }, - /** - * Cancels a previously enqueued `idlePeriod` callback. - * - * @memberof idlePeriod - * @param {number} handle Handle returned from `run` of callback to cancel - * @return {void} - */ - cancel(handle) { - window.cancelIdleCallback ? - window.cancelIdleCallback(handle) : - window.clearTimeout(handle); - } -}; -export {idlePeriod}; - -/** - * Async interface for enqueuing callbacks that run at microtask timing. - * - * Note that microtask timing is achieved via a single `MutationObserver`, - * and thus callbacks enqueued with this API will all run in a single - * batch, and not interleaved with other microtasks such as promises. - * Promises are avoided as an implementation choice for the time being - * due to Safari bugs that cause Promises to lack microtask guarantees. - * - * @namespace - * @summary Async interface for enqueuing callbacks that run at microtask - * timing. - */ -const microTask = { - - /** - * Enqueues a function called at microtask timing. - * - * @memberof microTask - * @param {!Function=} callback Callback to run - * @return {number} Handle used for canceling task - */ - run(callback) { - microtaskNode.textContent = microtaskNodeContent++; - microtaskCallbacks.push(callback); - return microtaskCurrHandle++; - }, - - /** - * Cancels a previously enqueued `microTask` callback. - * - * @memberof microTask - * @param {number} handle Handle returned from `run` of callback to cancel - * @return {void} - */ - cancel(handle) { - const idx = handle - microtaskLastHandle; - if (idx >= 0) { - if (!microtaskCallbacks[idx]) { - throw new Error('invalid async handle: ' + handle); - } - microtaskCallbacks[idx] = null; - } - } - -}; -export {microTask};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/boot.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/boot.js deleted file mode 100644 index d2bce39..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/boot.js +++ /dev/null
@@ -1,25 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -/* eslint-disable no-unused-vars */ -/** - * When using Closure Compiler, JSCompiler_renameProperty(property, object) is replaced by the munged name for object[property] - * We cannot alias this function, so we have to use a small shim that has the same behavior when not compiling. - * - * @param {string} prop Property name - * @param {?Object} obj Reference object - * @return {string} Potentially renamed property name - */ -window.JSCompiler_renameProperty = function(prop, obj) { - return prop; -}; -/* eslint-enable */ - -export {};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/case-map.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/case-map.js deleted file mode 100644 index 1963b02..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/case-map.js +++ /dev/null
@@ -1,47 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import './boot.js'; - -const caseMap = {}; -const DASH_TO_CAMEL = /-[a-z]/g; -const CAMEL_TO_DASH = /([A-Z])/g; - -/** - * @fileoverview Module with utilities for converting between "dash-case" and - * "camelCase" identifiers. - */ - -/** - * Converts "dash-case" identifier (e.g. `foo-bar-baz`) to "camelCase" - * (e.g. `fooBarBaz`). - * - * @param {string} dash Dash-case identifier - * @return {string} Camel-case representation of the identifier - */ -export function dashToCamelCase(dash) { - return caseMap[dash] || ( - caseMap[dash] = dash.indexOf('-') < 0 ? dash : dash.replace(DASH_TO_CAMEL, - (m) => m[1].toUpperCase() - ) - ); -} - -/** - * Converts "camelCase" identifier (e.g. `fooBarBaz`) to "dash-case" - * (e.g. `foo-bar-baz`). - * - * @param {string} camel Camel-case identifier - * @return {string} Dash-case representation of the identifier - */ -export function camelToDashCase(camel) { - return caseMap[camel] || ( - caseMap[camel] = camel.replace(CAMEL_TO_DASH, '-$1').toLowerCase() - ); -}
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/debounce.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/debounce.js deleted file mode 100644 index cf4311abbc..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/debounce.js +++ /dev/null
@@ -1,165 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import './boot.js'; - -import './mixin.js'; -import './async.js'; - -/** - * @summary Collapse multiple callbacks into one invocation after a timer. - */ -export class Debouncer { - constructor() { - this._asyncModule = null; - this._callback = null; - this._timer = null; - } - /** - * Sets the scheduler; that is, a module with the Async interface, - * a callback and optional arguments to be passed to the run function - * from the async module. - * - * @param {!AsyncInterface} asyncModule Object with Async interface. - * @param {function()} callback Callback to run. - * @return {void} - */ - setConfig(asyncModule, callback) { - this._asyncModule = asyncModule; - this._callback = callback; - this._timer = this._asyncModule.run(() => { - this._timer = null; - debouncerQueue.delete(this); - this._callback(); - }); - } - /** - * Cancels an active debouncer and returns a reference to itself. - * - * @return {void} - */ - cancel() { - if (this.isActive()) { - this._cancelAsync(); - // Canceling a debouncer removes its spot from the flush queue, - // so if a debouncer is manually canceled and re-debounced, it - // will reset its flush order (this is a very minor difference from 1.x) - // Re-debouncing via the `debounce` API retains the 1.x FIFO flush order - debouncerQueue.delete(this); - } - } - /** - * Cancels a debouncer's async callback. - * - * @return {void} - */ - _cancelAsync() { - if (this.isActive()) { - this._asyncModule.cancel(/** @type {number} */(this._timer)); - this._timer = null; - } - } - /** - * Flushes an active debouncer and returns a reference to itself. - * - * @return {void} - */ - flush() { - if (this.isActive()) { - this.cancel(); - this._callback(); - } - } - /** - * Returns true if the debouncer is active. - * - * @return {boolean} True if active. - */ - isActive() { - return this._timer != null; - } - /** - * Creates a debouncer if no debouncer is passed as a parameter - * or it cancels an active debouncer otherwise. The following - * example shows how a debouncer can be called multiple times within a - * microtask and "debounced" such that the provided callback function is - * called once. Add this method to a custom element: - * - * ```js - * import {microTask} from '@polymer/polymer/lib/utils/async.js'; - * import {Debouncer} from '@polymer/polymer/lib/utils/debounce.js'; - * // ... - * - * _debounceWork() { - * this._debounceJob = Debouncer.debounce(this._debounceJob, - * microTask, () => this._doWork()); - * } - * ``` - * - * If the `_debounceWork` method is called multiple times within the same - * microtask, the `_doWork` function will be called only once at the next - * microtask checkpoint. - * - * Note: In testing it is often convenient to avoid asynchrony. To accomplish - * this with a debouncer, you can use `enqueueDebouncer` and - * `flush`. For example, extend the above example by adding - * `enqueueDebouncer(this._debounceJob)` at the end of the - * `_debounceWork` method. Then in a test, call `flush` to ensure - * the debouncer has completed. - * - * @param {Debouncer?} debouncer Debouncer object. - * @param {!AsyncInterface} asyncModule Object with Async interface - * @param {function()} callback Callback to run. - * @return {!Debouncer} Returns a debouncer object. - */ - static debounce(debouncer, asyncModule, callback) { - if (debouncer instanceof Debouncer) { - // Cancel the async callback, but leave in debouncerQueue if it was - // enqueued, to maintain 1.x flush order - debouncer._cancelAsync(); - } else { - debouncer = new Debouncer(); - } - debouncer.setConfig(asyncModule, callback); - return debouncer; - } -} - -let debouncerQueue = new Set(); - -/** - * Adds a `Debouncer` to a list of globally flushable tasks. - * - * @param {!Debouncer} debouncer Debouncer to enqueue - * @return {void} - */ -export const enqueueDebouncer = function(debouncer) { - debouncerQueue.add(debouncer); -}; - -/** - * Flushes any enqueued debouncers - * - * @return {boolean} Returns whether any debouncers were flushed - */ -export const flushDebouncers = function() { - const didFlush = Boolean(debouncerQueue.size); - // If new debouncers are added while flushing, Set.forEach will ensure - // newly added ones are also flushed - debouncerQueue.forEach(debouncer => { - try { - debouncer.flush(); - } catch(e) { - setTimeout(() => { - throw e; - }); - } - }); - return didFlush; -}; \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flattened-nodes-observer.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flattened-nodes-observer.js deleted file mode 100644 index ddc8b74..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flattened-nodes-observer.js +++ /dev/null
@@ -1,314 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import './boot.js'; - -import { calculateSplices } from './array-splice.js'; -import { microTask } from './async.js'; -import { wrap } from './wrap.js'; - -/** - * Returns true if `node` is a slot element - * @param {!Node} node Node to test. - * @return {boolean} Returns true if the given `node` is a slot - * @private - */ -function isSlot(node) { - return (node.localName === 'slot'); -} - -/** - * Class that listens for changes (additions or removals) to - * "flattened nodes" on a given `node`. The list of flattened nodes consists - * of a node's children and, for any children that are `<slot>` elements, - * the expanded flattened list of `assignedNodes`. - * For example, if the observed node has children `<a></a><slot></slot><b></b>` - * and the `<slot>` has one `<div>` assigned to it, then the flattened - * nodes list is `<a></a><div></div><b></b>`. If the `<slot>` has other - * `<slot>` elements assigned to it, these are flattened as well. - * - * The provided `callback` is called whenever any change to this list - * of flattened nodes occurs, where an addition or removal of a node is - * considered a change. The `callback` is called with one argument, an object - * containing an array of any `addedNodes` and `removedNodes`. - * - * Note: the callback is called asynchronous to any changes - * at a microtask checkpoint. This is because observation is performed using - * `MutationObserver` and the `<slot>` element's `slotchange` event which - * are asynchronous. - * - * An example: - * ```js - * class TestSelfObserve extends PolymerElement { - * static get is() { return 'test-self-observe';} - * connectedCallback() { - * super.connectedCallback(); - * this._observer = new FlattenedNodesObserver(this, (info) => { - * this.info = info; - * }); - * } - * disconnectedCallback() { - * super.disconnectedCallback(); - * this._observer.disconnect(); - * } - * } - * customElements.define(TestSelfObserve.is, TestSelfObserve); - * ``` - * - * @summary Class that listens for changes (additions or removals) to - * "flattened nodes" on a given `node`. - * @implements {PolymerDomApi.ObserveHandle} - */ -export let FlattenedNodesObserver = class { - - /** - * Returns the list of flattened nodes for the given `node`. - * This list consists of a node's children and, for any children - * that are `<slot>` elements, the expanded flattened list of `assignedNodes`. - * For example, if the observed node has children `<a></a><slot></slot><b></b>` - * and the `<slot>` has one `<div>` assigned to it, then the flattened - * nodes list is `<a></a><div></div><b></b>`. If the `<slot>` has other - * `<slot>` elements assigned to it, these are flattened as well. - * - * @param {!HTMLElement|!HTMLSlotElement} node The node for which to - * return the list of flattened nodes. - * @return {!Array<!Node>} The list of flattened nodes for the given `node`. - * @nocollapse See https://github.com/google/closure-compiler/issues/2763 - */ - // eslint-disable-next-line - static getFlattenedNodes(node) { - const wrapped = wrap(node); - if (isSlot(node)) { - node = /** @type {!HTMLSlotElement} */(node); // eslint-disable-line no-self-assign - return wrapped.assignedNodes({flatten: true}); - } else { - return Array.from(wrapped.childNodes).map((node) => { - if (isSlot(node)) { - node = /** @type {!HTMLSlotElement} */(node); // eslint-disable-line no-self-assign - return wrap(node).assignedNodes({flatten: true}); - } else { - return [node]; - } - }).reduce((a, b) => a.concat(b), []); - } - } - - /** - * @param {!HTMLElement} target Node on which to listen for changes. - * @param {?function(this: Element, { target: !HTMLElement, addedNodes: !Array<!Element>, removedNodes: !Array<!Element> }):void} callback Function called when there are additions - * or removals from the target's list of flattened nodes. - */ - // eslint-disable-next-line - constructor(target, callback) { - /** - * @type {MutationObserver} - * @private - */ - this._shadyChildrenObserver = null; - /** - * @type {MutationObserver} - * @private - */ - this._nativeChildrenObserver = null; - this._connected = false; - /** - * @type {!HTMLElement} - * @private - */ - this._target = target; - this.callback = callback; - this._effectiveNodes = []; - this._observer = null; - this._scheduled = false; - /** - * @type {function()} - * @private - */ - this._boundSchedule = () => { - this._schedule(); - }; - this.connect(); - this._schedule(); - } - - /** - * Activates an observer. This method is automatically called when - * a `FlattenedNodesObserver` is created. It should only be called to - * re-activate an observer that has been deactivated via the `disconnect` method. - * - * @return {void} - */ - connect() { - if (isSlot(this._target)) { - this._listenSlots([this._target]); - } else if (wrap(this._target).children) { - this._listenSlots( - /** @type {!NodeList<!Node>} */ (wrap(this._target).children)); - if (window.ShadyDOM) { - this._shadyChildrenObserver = - ShadyDOM.observeChildren(this._target, (mutations) => { - this._processMutations(mutations); - }); - } else { - this._nativeChildrenObserver = - new MutationObserver((mutations) => { - this._processMutations(mutations); - }); - this._nativeChildrenObserver.observe(this._target, {childList: true}); - } - } - this._connected = true; - } - - /** - * Deactivates the flattened nodes observer. After calling this method - * the observer callback will not be called when changes to flattened nodes - * occur. The `connect` method may be subsequently called to reactivate - * the observer. - * - * @return {void} - * @override - */ - disconnect() { - if (isSlot(this._target)) { - this._unlistenSlots([this._target]); - } else if (wrap(this._target).children) { - this._unlistenSlots( - /** @type {!NodeList<!Node>} */ (wrap(this._target).children)); - if (window.ShadyDOM && this._shadyChildrenObserver) { - ShadyDOM.unobserveChildren(this._shadyChildrenObserver); - this._shadyChildrenObserver = null; - } else if (this._nativeChildrenObserver) { - this._nativeChildrenObserver.disconnect(); - this._nativeChildrenObserver = null; - } - } - this._connected = false; - } - - /** - * @return {void} - * @private - */ - _schedule() { - if (!this._scheduled) { - this._scheduled = true; - microTask.run(() => this.flush()); - } - } - - /** - * @param {Array<MutationRecord>} mutations Mutations signaled by the mutation observer - * @return {void} - * @private - */ - _processMutations(mutations) { - this._processSlotMutations(mutations); - this.flush(); - } - - /** - * @param {Array<MutationRecord>} mutations Mutations signaled by the mutation observer - * @return {void} - * @private - */ - _processSlotMutations(mutations) { - if (mutations) { - for (let i=0; i < mutations.length; i++) { - let mutation = mutations[i]; - if (mutation.addedNodes) { - this._listenSlots(mutation.addedNodes); - } - if (mutation.removedNodes) { - this._unlistenSlots(mutation.removedNodes); - } - } - } - } - - /** - * Flushes the observer causing any pending changes to be immediately - * delivered the observer callback. By default these changes are delivered - * asynchronously at the next microtask checkpoint. - * - * @return {boolean} Returns true if any pending changes caused the observer - * callback to run. - */ - flush() { - if (!this._connected) { - return false; - } - if (window.ShadyDOM) { - ShadyDOM.flush(); - } - if (this._nativeChildrenObserver) { - this._processSlotMutations(this._nativeChildrenObserver.takeRecords()); - } else if (this._shadyChildrenObserver) { - this._processSlotMutations(this._shadyChildrenObserver.takeRecords()); - } - this._scheduled = false; - let info = { - target: this._target, - addedNodes: [], - removedNodes: [] - }; - let newNodes = this.constructor.getFlattenedNodes(this._target); - let splices = calculateSplices(newNodes, - this._effectiveNodes); - // process removals - for (let i=0, s; (i<splices.length) && (s=splices[i]); i++) { - for (let j=0, n; (j < s.removed.length) && (n=s.removed[j]); j++) { - info.removedNodes.push(n); - } - } - // process adds - for (let i=0, s; (i<splices.length) && (s=splices[i]); i++) { - for (let j=s.index; j < s.index + s.addedCount; j++) { - info.addedNodes.push(newNodes[j]); - } - } - // update cache - this._effectiveNodes = newNodes; - let didFlush = false; - if (info.addedNodes.length || info.removedNodes.length) { - didFlush = true; - this.callback.call(this._target, info); - } - return didFlush; - } - - /** - * @param {!Array<!Node>|!NodeList<!Node>} nodeList Nodes that could change - * @return {void} - * @private - */ - _listenSlots(nodeList) { - for (let i=0; i < nodeList.length; i++) { - let n = nodeList[i]; - if (isSlot(n)) { - n.addEventListener('slotchange', this._boundSchedule); - } - } - } - - /** - * @param {!Array<!Node>|!NodeList<!Node>} nodeList Nodes that could change - * @return {void} - * @private - */ - _unlistenSlots(nodeList) { - for (let i=0; i < nodeList.length; i++) { - let n = nodeList[i]; - if (isSlot(n)) { - n.removeEventListener('slotchange', this._boundSchedule); - } - } - } - -}; \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flush.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flush.js deleted file mode 100644 index a7190470..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flush.js +++ /dev/null
@@ -1,33 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import './boot.js'; -/* eslint-disable no-unused-vars */ -import { Debouncer } from '../utils/debounce.js'; // used in type annotations -/* eslint-enable no-unused-vars */ -import { flushDebouncers } from '../utils/debounce.js'; // used in type annotations -export { enqueueDebouncer } from '../utils/debounce.js'; // used in type annotations - -/** - * Forces several classes of asynchronously queued tasks to flush: - * - Debouncers added via `enqueueDebouncer` - * - ShadyDOM distribution - * - * @return {void} - */ -export const flush = function() { - let shadyDOM, debouncers; - do { - shadyDOM = window.ShadyDOM && ShadyDOM.flush(); - if (window.ShadyCSS && window.ShadyCSS.ScopingShim) { - window.ShadyCSS.ScopingShim.flush(); - } - debouncers = flushDebouncers(); - } while (shadyDOM || debouncers); -};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/gestures.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/gestures.js deleted file mode 100644 index f5f4bfd..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/gestures.js +++ /dev/null
@@ -1,1079 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -/** - * @fileoverview - * - * Module for adding listeners to a node for the following normalized - * cross-platform "gesture" events: - * - `down` - mouse or touch went down - * - `up` - mouse or touch went up - * - `tap` - mouse click or finger tap - * - `track` - mouse drag or touch move - * - * @summary Module for adding cross-platform gesture event listeners. - */ - -import './boot.js'; - -import { timeOut, microTask } from './async.js'; -import { Debouncer } from './debounce.js'; -import { passiveTouchGestures } from './settings.js'; -import { wrap } from './wrap.js'; - -// detect native touch action support -let HAS_NATIVE_TA = typeof document.head.style.touchAction === 'string'; -let GESTURE_KEY = '__polymerGestures'; -let HANDLED_OBJ = '__polymerGesturesHandled'; -let TOUCH_ACTION = '__polymerGesturesTouchAction'; -// radius for tap and track -let TAP_DISTANCE = 25; -let TRACK_DISTANCE = 5; -// number of last N track positions to keep -let TRACK_LENGTH = 2; - -// Disabling "mouse" handlers for 2500ms is enough -let MOUSE_TIMEOUT = 2500; -let MOUSE_EVENTS = ['mousedown', 'mousemove', 'mouseup', 'click']; -// an array of bitmask values for mapping MouseEvent.which to MouseEvent.buttons -let MOUSE_WHICH_TO_BUTTONS = [0, 1, 4, 2]; -let MOUSE_HAS_BUTTONS = (function() { - try { - return new MouseEvent('test', {buttons: 1}).buttons === 1; - } catch (e) { - return false; - } -})(); - -/** - * @param {string} name Possible mouse event name - * @return {boolean} true if mouse event, false if not - */ -function isMouseEvent(name) { - return MOUSE_EVENTS.indexOf(name) > -1; -} - -/* eslint no-empty: ["error", { "allowEmptyCatch": true }] */ -// check for passive event listeners -let SUPPORTS_PASSIVE = false; -(function() { - try { - let opts = Object.defineProperty({}, 'passive', {get() {SUPPORTS_PASSIVE = true;}}); - window.addEventListener('test', null, opts); - window.removeEventListener('test', null, opts); - } catch(e) {} -})(); - -/** - * Generate settings for event listeners, dependant on `passiveTouchGestures` - * - * @param {string} eventName Event name to determine if `{passive}` option is - * needed - * @return {{passive: boolean} | undefined} Options to use for addEventListener - * and removeEventListener - */ -function PASSIVE_TOUCH(eventName) { - if (isMouseEvent(eventName) || eventName === 'touchend') { - return; - } - if (HAS_NATIVE_TA && SUPPORTS_PASSIVE && passiveTouchGestures) { - return {passive: true}; - } else { - return; - } -} - -// Check for touch-only devices -let IS_TOUCH_ONLY = navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/); - -// keep track of any labels hit by the mouseCanceller -/** @type {!Array<!HTMLLabelElement>} */ -const clickedLabels = []; - -/** @type {!Object<boolean>} */ -const labellable = { - 'button': true, - 'input': true, - 'keygen': true, - 'meter': true, - 'output': true, - 'textarea': true, - 'progress': true, - 'select': true -}; - -// Defined at https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#enabling-and-disabling-form-controls:-the-disabled-attribute -/** @type {!Object<boolean>} */ -const canBeDisabled = { - 'button': true, - 'command': true, - 'fieldset': true, - 'input': true, - 'keygen': true, - 'optgroup': true, - 'option': true, - 'select': true, - 'textarea': true -}; - -/** - * @param {HTMLElement} el Element to check labelling status - * @return {boolean} element can have labels - */ -function canBeLabelled(el) { - return labellable[el.localName] || false; -} - -/** - * @param {HTMLElement} el Element that may be labelled. - * @return {!Array<!HTMLLabelElement>} Relevant label for `el` - */ -function matchingLabels(el) { - let labels = Array.prototype.slice.call(/** @type {HTMLInputElement} */(el).labels || []); - // IE doesn't have `labels` and Safari doesn't populate `labels` - // if element is in a shadowroot. - // In this instance, finding the non-ancestor labels is enough, - // as the mouseCancellor code will handle ancstor labels - if (!labels.length) { - labels = []; - let root = el.getRootNode(); - // if there is an id on `el`, check for all labels with a matching `for` attribute - if (el.id) { - let matching = root.querySelectorAll(`label[for = ${el.id}]`); - for (let i = 0; i < matching.length; i++) { - labels.push(/** @type {!HTMLLabelElement} */(matching[i])); - } - } - } - return labels; -} - -// touch will make synthetic mouse events -// `preventDefault` on touchend will cancel them, -// but this breaks `<input>` focus and link clicks -// disable mouse handlers for MOUSE_TIMEOUT ms after -// a touchend to ignore synthetic mouse events -let mouseCanceller = function(mouseEvent) { - // Check for sourceCapabilities, used to distinguish synthetic events - // if mouseEvent did not come from a device that fires touch events, - // it was made by a real mouse and should be counted - // http://wicg.github.io/InputDeviceCapabilities/#dom-inputdevicecapabilities-firestouchevents - let sc = mouseEvent.sourceCapabilities; - if (sc && !sc.firesTouchEvents) { - return; - } - // skip synthetic mouse events - mouseEvent[HANDLED_OBJ] = {skip: true}; - // disable "ghost clicks" - if (mouseEvent.type === 'click') { - let clickFromLabel = false; - let path = getComposedPath(mouseEvent); - for (let i = 0; i < path.length; i++) { - if (path[i].nodeType === Node.ELEMENT_NODE) { - if (path[i].localName === 'label') { - clickedLabels.push(/** @type {!HTMLLabelElement} */ (path[i])); - } else if (canBeLabelled(/** @type {!HTMLElement} */ (path[i]))) { - let ownerLabels = - matchingLabels(/** @type {!HTMLElement} */ (path[i])); - // check if one of the clicked labels is labelling this element - for (let j = 0; j < ownerLabels.length; j++) { - clickFromLabel = clickFromLabel || clickedLabels.indexOf(ownerLabels[j]) > -1; - } - } - } - if (path[i] === POINTERSTATE.mouse.target) { - return; - } - } - // if one of the clicked labels was labelling the target element, - // this is not a ghost click - if (clickFromLabel) { - return; - } - mouseEvent.preventDefault(); - mouseEvent.stopPropagation(); - } -}; - -/** - * @param {boolean=} setup True to add, false to remove. - * @return {void} - */ -function setupTeardownMouseCanceller(setup) { - let events = IS_TOUCH_ONLY ? ['click'] : MOUSE_EVENTS; - for (let i = 0, en; i < events.length; i++) { - en = events[i]; - if (setup) { - // reset clickLabels array - clickedLabels.length = 0; - document.addEventListener(en, mouseCanceller, true); - } else { - document.removeEventListener(en, mouseCanceller, true); - } - } -} - -function ignoreMouse(e) { - if (!POINTERSTATE.mouse.mouseIgnoreJob) { - setupTeardownMouseCanceller(true); - } - let unset = function() { - setupTeardownMouseCanceller(); - POINTERSTATE.mouse.target = null; - POINTERSTATE.mouse.mouseIgnoreJob = null; - }; - POINTERSTATE.mouse.target = getComposedPath(e)[0]; - POINTERSTATE.mouse.mouseIgnoreJob = Debouncer.debounce( - POINTERSTATE.mouse.mouseIgnoreJob - , timeOut.after(MOUSE_TIMEOUT) - , unset); -} - -/** - * @param {MouseEvent} ev event to test for left mouse button down - * @return {boolean} has left mouse button down - */ -function hasLeftMouseButton(ev) { - let type = ev.type; - // exit early if the event is not a mouse event - if (!isMouseEvent(type)) { - return false; - } - // ev.button is not reliable for mousemove (0 is overloaded as both left button and no buttons) - // instead we use ev.buttons (bitmask of buttons) or fall back to ev.which (deprecated, 0 for no buttons, 1 for left button) - if (type === 'mousemove') { - // allow undefined for testing events - let buttons = ev.buttons === undefined ? 1 : ev.buttons; - if ((ev instanceof window.MouseEvent) && !MOUSE_HAS_BUTTONS) { - buttons = MOUSE_WHICH_TO_BUTTONS[ev.which] || 0; - } - // buttons is a bitmask, check that the left button bit is set (1) - return Boolean(buttons & 1); - } else { - // allow undefined for testing events - let button = ev.button === undefined ? 0 : ev.button; - // ev.button is 0 in mousedown/mouseup/click for left button activation - return button === 0; - } -} - -function isSyntheticClick(ev) { - if (ev.type === 'click') { - // ev.detail is 0 for HTMLElement.click in most browsers - if (ev.detail === 0) { - return true; - } - // in the worst case, check that the x/y position of the click is within - // the bounding box of the target of the event - // Thanks IE 10 >:( - let t = _findOriginalTarget(ev); - // make sure the target of the event is an element so we can use getBoundingClientRect, - // if not, just assume it is a synthetic click - if (!t.nodeType || /** @type {Element} */(t).nodeType !== Node.ELEMENT_NODE) { - return true; - } - let bcr = /** @type {Element} */(t).getBoundingClientRect(); - // use page x/y to account for scrolling - let x = ev.pageX, y = ev.pageY; - // ev is a synthetic click if the position is outside the bounding box of the target - return !((x >= bcr.left && x <= bcr.right) && (y >= bcr.top && y <= bcr.bottom)); - } - return false; -} - -let POINTERSTATE = { - mouse: { - target: null, - mouseIgnoreJob: null - }, - touch: { - x: 0, - y: 0, - id: -1, - scrollDecided: false - } -}; - -function firstTouchAction(ev) { - let ta = 'auto'; - let path = getComposedPath(ev); - for (let i = 0, n; i < path.length; i++) { - n = path[i]; - if (n[TOUCH_ACTION]) { - ta = n[TOUCH_ACTION]; - break; - } - } - return ta; -} - -function trackDocument(stateObj, movefn, upfn) { - stateObj.movefn = movefn; - stateObj.upfn = upfn; - document.addEventListener('mousemove', movefn); - document.addEventListener('mouseup', upfn); -} - -function untrackDocument(stateObj) { - document.removeEventListener('mousemove', stateObj.movefn); - document.removeEventListener('mouseup', stateObj.upfn); - stateObj.movefn = null; - stateObj.upfn = null; -} - -// use a document-wide touchend listener to start the ghost-click prevention mechanism -// Use passive event listeners, if supported, to not affect scrolling performance -document.addEventListener('touchend', ignoreMouse, SUPPORTS_PASSIVE ? {passive: true} : false); - -/** - * Returns the composedPath for the given event. - * @param {Event} event to process - * @return {!Array<!EventTarget>} Path of the event - */ -const getComposedPath = window.ShadyDOM && window.ShadyDOM.noPatch ? - window.ShadyDOM.composedPath : - (event) => event.composedPath && event.composedPath() || []; - -/** @type {!Object<string, !GestureRecognizer>} */ -export const gestures = {}; - -/** @type {!Array<!GestureRecognizer>} */ -export const recognizers = []; - -/** - * Finds the element rendered on the screen at the provided coordinates. - * - * Similar to `document.elementFromPoint`, but pierces through - * shadow roots. - * - * @param {number} x Horizontal pixel coordinate - * @param {number} y Vertical pixel coordinate - * @return {Element} Returns the deepest shadowRoot inclusive element - * found at the screen position given. - */ -export function deepTargetFind(x, y) { - let node = document.elementFromPoint(x, y); - let next = node; - // this code path is only taken when native ShadowDOM is used - // if there is a shadowroot, it may have a node at x/y - // if there is not a shadowroot, exit the loop - while (next && next.shadowRoot && !window.ShadyDOM) { - // if there is a node at x/y in the shadowroot, look deeper - let oldNext = next; - next = next.shadowRoot.elementFromPoint(x, y); - // on Safari, elementFromPoint may return the shadowRoot host - if (oldNext === next) { - break; - } - if (next) { - node = next; - } - } - return node; -} - -/** - * a cheaper check than ev.composedPath()[0]; - * - * @private - * @param {Event|Touch} ev Event. - * @return {EventTarget} Returns the event target. - */ -function _findOriginalTarget(ev) { - const path = getComposedPath(/** @type {?Event} */ (ev)); - // It shouldn't be, but sometimes path is empty (window on Safari). - return path.length > 0 ? path[0] : ev.target; -} - -/** - * @private - * @param {Event} ev Event. - * @return {void} - */ -function _handleNative(ev) { - let handled; - let type = ev.type; - let node = ev.currentTarget; - let gobj = node[GESTURE_KEY]; - if (!gobj) { - return; - } - let gs = gobj[type]; - if (!gs) { - return; - } - if (!ev[HANDLED_OBJ]) { - ev[HANDLED_OBJ] = {}; - if (type.slice(0, 5) === 'touch') { - ev = /** @type {TouchEvent} */(ev); // eslint-disable-line no-self-assign - let t = ev.changedTouches[0]; - if (type === 'touchstart') { - // only handle the first finger - if (ev.touches.length === 1) { - POINTERSTATE.touch.id = t.identifier; - } - } - if (POINTERSTATE.touch.id !== t.identifier) { - return; - } - if (!HAS_NATIVE_TA) { - if (type === 'touchstart' || type === 'touchmove') { - _handleTouchAction(ev); - } - } - } - } - handled = ev[HANDLED_OBJ]; - // used to ignore synthetic mouse events - if (handled.skip) { - return; - } - // reset recognizer state - for (let i = 0, r; i < recognizers.length; i++) { - r = recognizers[i]; - if (gs[r.name] && !handled[r.name]) { - if (r.flow && r.flow.start.indexOf(ev.type) > -1 && r.reset) { - r.reset(); - } - } - } - // enforce gesture recognizer order - for (let i = 0, r; i < recognizers.length; i++) { - r = recognizers[i]; - if (gs[r.name] && !handled[r.name]) { - handled[r.name] = true; - r[type](ev); - } - } -} - -/** - * @private - * @param {TouchEvent} ev Event. - * @return {void} - */ -function _handleTouchAction(ev) { - let t = ev.changedTouches[0]; - let type = ev.type; - if (type === 'touchstart') { - POINTERSTATE.touch.x = t.clientX; - POINTERSTATE.touch.y = t.clientY; - POINTERSTATE.touch.scrollDecided = false; - } else if (type === 'touchmove') { - if (POINTERSTATE.touch.scrollDecided) { - return; - } - POINTERSTATE.touch.scrollDecided = true; - let ta = firstTouchAction(ev); - let shouldPrevent = false; - let dx = Math.abs(POINTERSTATE.touch.x - t.clientX); - let dy = Math.abs(POINTERSTATE.touch.y - t.clientY); - if (!ev.cancelable) { - // scrolling is happening - } else if (ta === 'none') { - shouldPrevent = true; - } else if (ta === 'pan-x') { - shouldPrevent = dy > dx; - } else if (ta === 'pan-y') { - shouldPrevent = dx > dy; - } - if (shouldPrevent) { - ev.preventDefault(); - } else { - prevent('track'); - } - } -} - -/** - * Adds an event listener to a node for the given gesture type. - * - * @param {!EventTarget} node Node to add listener on - * @param {string} evType Gesture type: `down`, `up`, `track`, or `tap` - * @param {!function(!Event):void} handler Event listener function to call - * @return {boolean} Returns true if a gesture event listener was added. - */ -export function addListener(node, evType, handler) { - if (gestures[evType]) { - _add(node, evType, handler); - return true; - } - return false; -} - -/** - * Removes an event listener from a node for the given gesture type. - * - * @param {!EventTarget} node Node to remove listener from - * @param {string} evType Gesture type: `down`, `up`, `track`, or `tap` - * @param {!function(!Event):void} handler Event listener function previously passed to - * `addListener`. - * @return {boolean} Returns true if a gesture event listener was removed. - */ -export function removeListener(node, evType, handler) { - if (gestures[evType]) { - _remove(node, evType, handler); - return true; - } - return false; -} - -/** - * automate the event listeners for the native events - * - * @private - * @param {!EventTarget} node Node on which to add the event. - * @param {string} evType Event type to add. - * @param {function(!Event)} handler Event handler function. - * @return {void} - */ -function _add(node, evType, handler) { - let recognizer = gestures[evType]; - let deps = recognizer.deps; - let name = recognizer.name; - let gobj = node[GESTURE_KEY]; - if (!gobj) { - node[GESTURE_KEY] = gobj = {}; - } - for (let i = 0, dep, gd; i < deps.length; i++) { - dep = deps[i]; - // don't add mouse handlers on iOS because they cause gray selection overlays - if (IS_TOUCH_ONLY && isMouseEvent(dep) && dep !== 'click') { - continue; - } - gd = gobj[dep]; - if (!gd) { - gobj[dep] = gd = {_count: 0}; - } - if (gd._count === 0) { - node.addEventListener(dep, _handleNative, PASSIVE_TOUCH(dep)); - } - gd[name] = (gd[name] || 0) + 1; - gd._count = (gd._count || 0) + 1; - } - node.addEventListener(evType, handler); - if (recognizer.touchAction) { - setTouchAction(node, recognizer.touchAction); - } -} - -/** - * automate event listener removal for native events - * - * @private - * @param {!EventTarget} node Node on which to remove the event. - * @param {string} evType Event type to remove. - * @param {function(!Event): void} handler Event handler function. - * @return {void} - */ -function _remove(node, evType, handler) { - let recognizer = gestures[evType]; - let deps = recognizer.deps; - let name = recognizer.name; - let gobj = node[GESTURE_KEY]; - if (gobj) { - for (let i = 0, dep, gd; i < deps.length; i++) { - dep = deps[i]; - gd = gobj[dep]; - if (gd && gd[name]) { - gd[name] = (gd[name] || 1) - 1; - gd._count = (gd._count || 1) - 1; - if (gd._count === 0) { - node.removeEventListener(dep, _handleNative, PASSIVE_TOUCH(dep)); - } - } - } - } - node.removeEventListener(evType, handler); -} - -/** - * Registers a new gesture event recognizer for adding new custom - * gesture event types. - * - * @param {!GestureRecognizer} recog Gesture recognizer descriptor - * @return {void} - */ -export function register(recog) { - recognizers.push(recog); - for (let i = 0; i < recog.emits.length; i++) { - gestures[recog.emits[i]] = recog; - } -} - -/** - * @private - * @param {string} evName Event name. - * @return {Object} Returns the gesture for the given event name. - */ -function _findRecognizerByEvent(evName) { - for (let i = 0, r; i < recognizers.length; i++) { - r = recognizers[i]; - for (let j = 0, n; j < r.emits.length; j++) { - n = r.emits[j]; - if (n === evName) { - return r; - } - } - } - return null; -} - -/** - * Sets scrolling direction on node. - * - * This value is checked on first move, thus it should be called prior to - * adding event listeners. - * - * @param {!EventTarget} node Node to set touch action setting on - * @param {string} value Touch action value - * @return {void} - */ -export function setTouchAction(node, value) { - if (HAS_NATIVE_TA && node instanceof HTMLElement) { - // NOTE: add touchAction async so that events can be added in - // custom element constructors. Otherwise we run afoul of custom - // elements restriction against settings attributes (style) in the - // constructor. - microTask.run(() => { - node.style.touchAction = value; - }); - } - node[TOUCH_ACTION] = value; -} - -/** - * Dispatches an event on the `target` element of `type` with the given - * `detail`. - * @private - * @param {!EventTarget} target The element on which to fire an event. - * @param {string} type The type of event to fire. - * @param {!Object=} detail The detail object to populate on the event. - * @return {void} - */ -function _fire(target, type, detail) { - let ev = new Event(type, { bubbles: true, cancelable: true, composed: true }); - ev.detail = detail; - wrap(/** @type {!Node} */(target)).dispatchEvent(ev); - // forward `preventDefault` in a clean way - if (ev.defaultPrevented) { - let preventer = detail.preventer || detail.sourceEvent; - if (preventer && preventer.preventDefault) { - preventer.preventDefault(); - } - } -} - -/** - * Prevents the dispatch and default action of the given event name. - * - * @param {string} evName Event name. - * @return {void} - */ -export function prevent(evName) { - let recognizer = _findRecognizerByEvent(evName); - if (recognizer.info) { - recognizer.info.prevent = true; - } -} - -/** - * Reset the 2500ms timeout on processing mouse input after detecting touch input. - * - * Touch inputs create synthesized mouse inputs anywhere from 0 to 2000ms after the touch. - * This method should only be called during testing with simulated touch inputs. - * Calling this method in production may cause duplicate taps or other Gestures. - * - * @return {void} - */ -export function resetMouseCanceller() { - if (POINTERSTATE.mouse.mouseIgnoreJob) { - POINTERSTATE.mouse.mouseIgnoreJob.flush(); - } -} - -/* eslint-disable valid-jsdoc */ - -register({ - name: 'downup', - deps: ['mousedown', 'touchstart', 'touchend'], - flow: { - start: ['mousedown', 'touchstart'], - end: ['mouseup', 'touchend'] - }, - emits: ['down', 'up'], - - info: { - movefn: null, - upfn: null - }, - - /** - * @this {GestureRecognizer} - * @return {void} - */ - reset: function() { - untrackDocument(this.info); - }, - - /** - * @this {GestureRecognizer} - * @param {MouseEvent} e - * @return {void} - */ - mousedown: function(e) { - if (!hasLeftMouseButton(e)) { - return; - } - let t = _findOriginalTarget(e); - let self = this; - let movefn = function movefn(e) { - if (!hasLeftMouseButton(e)) { - downupFire('up', t, e); - untrackDocument(self.info); - } - }; - let upfn = function upfn(e) { - if (hasLeftMouseButton(e)) { - downupFire('up', t, e); - } - untrackDocument(self.info); - }; - trackDocument(this.info, movefn, upfn); - downupFire('down', t, e); - }, - /** - * @this {GestureRecognizer} - * @param {TouchEvent} e - * @return {void} - */ - touchstart: function(e) { - downupFire('down', _findOriginalTarget(e), e.changedTouches[0], e); - }, - /** - * @this {GestureRecognizer} - * @param {TouchEvent} e - * @return {void} - */ - touchend: function(e) { - downupFire('up', _findOriginalTarget(e), e.changedTouches[0], e); - } -}); - -/** - * @param {string} type - * @param {EventTarget} target - * @param {Event|Touch} event - * @param {Event=} preventer - * @return {void} - */ -function downupFire(type, target, event, preventer) { - if (!target) { - return; - } - _fire(target, type, { - x: event.clientX, - y: event.clientY, - sourceEvent: event, - preventer: preventer, - prevent: function(e) { - return prevent(e); - } - }); -} - -register({ - name: 'track', - touchAction: 'none', - deps: ['mousedown', 'touchstart', 'touchmove', 'touchend'], - flow: { - start: ['mousedown', 'touchstart'], - end: ['mouseup', 'touchend'] - }, - emits: ['track'], - - info: { - x: 0, - y: 0, - state: 'start', - started: false, - moves: [], - /** @this {GestureInfo} */ - addMove: function(move) { - if (this.moves.length > TRACK_LENGTH) { - this.moves.shift(); - } - this.moves.push(move); - }, - movefn: null, - upfn: null, - prevent: false - }, - - /** - * @this {GestureRecognizer} - * @return {void} - */ - reset: function() { - this.info.state = 'start'; - this.info.started = false; - this.info.moves = []; - this.info.x = 0; - this.info.y = 0; - this.info.prevent = false; - untrackDocument(this.info); - }, - - /** - * @this {GestureRecognizer} - * @param {MouseEvent} e - * @return {void} - */ - mousedown: function(e) { - if (!hasLeftMouseButton(e)) { - return; - } - let t = _findOriginalTarget(e); - let self = this; - let movefn = function movefn(e) { - let x = e.clientX, y = e.clientY; - if (trackHasMovedEnough(self.info, x, y)) { - // first move is 'start', subsequent moves are 'move', mouseup is 'end' - self.info.state = self.info.started ? (e.type === 'mouseup' ? 'end' : 'track') : 'start'; - if (self.info.state === 'start') { - // if and only if tracking, always prevent tap - prevent('tap'); - } - self.info.addMove({x: x, y: y}); - if (!hasLeftMouseButton(e)) { - // always fire "end" - self.info.state = 'end'; - untrackDocument(self.info); - } - if (t) { - trackFire(self.info, t, e); - } - self.info.started = true; - } - }; - let upfn = function upfn(e) { - if (self.info.started) { - movefn(e); - } - - // remove the temporary listeners - untrackDocument(self.info); - }; - // add temporary document listeners as mouse retargets - trackDocument(this.info, movefn, upfn); - this.info.x = e.clientX; - this.info.y = e.clientY; - }, - /** - * @this {GestureRecognizer} - * @param {TouchEvent} e - * @return {void} - */ - touchstart: function(e) { - let ct = e.changedTouches[0]; - this.info.x = ct.clientX; - this.info.y = ct.clientY; - }, - /** - * @this {GestureRecognizer} - * @param {TouchEvent} e - * @return {void} - */ - touchmove: function(e) { - let t = _findOriginalTarget(e); - let ct = e.changedTouches[0]; - let x = ct.clientX, y = ct.clientY; - if (trackHasMovedEnough(this.info, x, y)) { - if (this.info.state === 'start') { - // if and only if tracking, always prevent tap - prevent('tap'); - } - this.info.addMove({x: x, y: y}); - trackFire(this.info, t, ct); - this.info.state = 'track'; - this.info.started = true; - } - }, - /** - * @this {GestureRecognizer} - * @param {TouchEvent} e - * @return {void} - */ - touchend: function(e) { - let t = _findOriginalTarget(e); - let ct = e.changedTouches[0]; - // only trackend if track was started and not aborted - if (this.info.started) { - // reset started state on up - this.info.state = 'end'; - this.info.addMove({x: ct.clientX, y: ct.clientY}); - trackFire(this.info, t, ct); - } - } -}); - -/** - * @param {!GestureInfo} info - * @param {number} x - * @param {number} y - * @return {boolean} - */ -function trackHasMovedEnough(info, x, y) { - if (info.prevent) { - return false; - } - if (info.started) { - return true; - } - let dx = Math.abs(info.x - x); - let dy = Math.abs(info.y - y); - return (dx >= TRACK_DISTANCE || dy >= TRACK_DISTANCE); -} - -/** - * @param {!GestureInfo} info - * @param {?EventTarget} target - * @param {Touch} touch - * @return {void} - */ -function trackFire(info, target, touch) { - if (!target) { - return; - } - let secondlast = info.moves[info.moves.length - 2]; - let lastmove = info.moves[info.moves.length - 1]; - let dx = lastmove.x - info.x; - let dy = lastmove.y - info.y; - let ddx, ddy = 0; - if (secondlast) { - ddx = lastmove.x - secondlast.x; - ddy = lastmove.y - secondlast.y; - } - _fire(target, 'track', { - state: info.state, - x: touch.clientX, - y: touch.clientY, - dx: dx, - dy: dy, - ddx: ddx, - ddy: ddy, - sourceEvent: touch, - hover: function() { - return deepTargetFind(touch.clientX, touch.clientY); - } - }); -} - -register({ - name: 'tap', - deps: ['mousedown', 'click', 'touchstart', 'touchend'], - flow: { - start: ['mousedown', 'touchstart'], - end: ['click', 'touchend'] - }, - emits: ['tap'], - info: { - x: NaN, - y: NaN, - prevent: false - }, - /** - * @this {GestureRecognizer} - * @return {void} - */ - reset: function() { - this.info.x = NaN; - this.info.y = NaN; - this.info.prevent = false; - }, - /** - * @this {GestureRecognizer} - * @param {MouseEvent} e - * @return {void} - */ - mousedown: function(e) { - if (hasLeftMouseButton(e)) { - this.info.x = e.clientX; - this.info.y = e.clientY; - } - }, - /** - * @this {GestureRecognizer} - * @param {MouseEvent} e - * @return {void} - */ - click: function(e) { - if (hasLeftMouseButton(e)) { - trackForward(this.info, e); - } - }, - /** - * @this {GestureRecognizer} - * @param {TouchEvent} e - * @return {void} - */ - touchstart: function(e) { - const touch = e.changedTouches[0]; - this.info.x = touch.clientX; - this.info.y = touch.clientY; - }, - /** - * @this {GestureRecognizer} - * @param {TouchEvent} e - * @return {void} - */ - touchend: function(e) { - trackForward(this.info, e.changedTouches[0], e); - } -}); - -/** - * @param {!GestureInfo} info - * @param {Event | Touch} e - * @param {Event=} preventer - * @return {void} - */ -function trackForward(info, e, preventer) { - let dx = Math.abs(e.clientX - info.x); - let dy = Math.abs(e.clientY - info.y); - // find original target from `preventer` for TouchEvents, or `e` for MouseEvents - let t = _findOriginalTarget((preventer || e)); - if (!t || (canBeDisabled[/** @type {!HTMLElement} */(t).localName] && t.hasAttribute('disabled'))) { - return; - } - // dx,dy can be NaN if `click` has been simulated and there was no `down` for `start` - if (isNaN(dx) || isNaN(dy) || (dx <= TAP_DISTANCE && dy <= TAP_DISTANCE) || isSyntheticClick(e)) { - // prevent taps from being generated if an event has canceled them - if (!info.prevent) { - _fire(t, 'tap', { - x: e.clientX, - y: e.clientY, - sourceEvent: e, - preventer: preventer - }); - } - } -} - -/* eslint-enable valid-jsdoc */ - -/** @deprecated */ -export const findOriginalTarget = _findOriginalTarget; - -/** @deprecated */ -export const add = addListener; - -/** @deprecated */ -export const remove = removeListener;
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/html-tag.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/html-tag.js deleted file mode 100644 index 3b0e3de..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/html-tag.js +++ /dev/null
@@ -1,128 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import './boot.js'; - -/** - * Class representing a static string value which can be used to filter - * strings by asseting that they have been created via this class. The - * `value` property returns the string passed to the constructor. - */ -class LiteralString { - constructor(string) { - /** @type {string} */ - this.value = string.toString(); - } - /** - * @return {string} LiteralString string value - * @override - */ - toString() { - return this.value; - } -} - -/** - * @param {*} value Object to stringify into HTML - * @return {string} HTML stringified form of `obj` - */ -function literalValue(value) { - if (value instanceof LiteralString) { - return /** @type {!LiteralString} */(value).value; - } else { - throw new Error( - `non-literal value passed to Polymer's htmlLiteral function: ${value}` - ); - } -} - -/** - * @param {*} value Object to stringify into HTML - * @return {string} HTML stringified form of `obj` - */ -function htmlValue(value) { - if (value instanceof HTMLTemplateElement) { - return /** @type {!HTMLTemplateElement } */(value).innerHTML; - } else if (value instanceof LiteralString) { - return literalValue(value); - } else { - throw new Error( - `non-template value passed to Polymer's html function: ${value}`); - } -} - -/** - * A template literal tag that creates an HTML <template> element from the - * contents of the string. - * - * This allows you to write a Polymer Template in JavaScript. - * - * Templates can be composed by interpolating `HTMLTemplateElement`s in - * expressions in the JavaScript template literal. The nested template's - * `innerHTML` is included in the containing template. The only other - * values allowed in expressions are those returned from `htmlLiteral` - * which ensures only literal values from JS source ever reach the HTML, to - * guard against XSS risks. - * - * All other values are disallowed in expressions to help prevent XSS - * attacks; however, `htmlLiteral` can be used to compose static - * string values into templates. This is useful to compose strings into - * places that do not accept html, like the css text of a `style` - * element. - * - * Example: - * - * static get template() { - * return html` - * <style>:host{ content:"..." }</style> - * <div class="shadowed">${this.partialTemplate}</div> - * ${super.template} - * `; - * } - * static get partialTemplate() { return html`<span>Partial!</span>`; } - * - * @param {!ITemplateArray} strings Constant parts of tagged template literal - * @param {...*} values Variable parts of tagged template literal - * @return {!HTMLTemplateElement} Constructed HTMLTemplateElement - */ -export const html = function html(strings, ...values) { - const template = /** @type {!HTMLTemplateElement} */(document.createElement('template')); - template.innerHTML = values.reduce((acc, v, idx) => - acc + htmlValue(v) + strings[idx + 1], strings[0]); - return template; -}; - -/** - * An html literal tag that can be used with `html` to compose. - * a literal string. - * - * Example: - * - * static get template() { - * return html` - * <style> - * :host { display: block; } - * ${this.styleTemplate()} - * </style> - * <div class="shadowed">${staticValue}</div> - * ${super.template} - * `; - * } - * static get styleTemplate() { - * return htmlLiteral`.shadowed { background: gray; }`; - * } - * - * @param {!ITemplateArray} strings Constant parts of tagged template literal - * @param {...*} values Variable parts of tagged template literal - * @return {!LiteralString} Constructed literal string - */ -export const htmlLiteral = function(strings, ...values) { - return new LiteralString(values.reduce((acc, v, idx) => - acc + literalValue(v) + strings[idx + 1], strings[0])); -};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/mixin.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/mixin.js deleted file mode 100644 index b79c7f9..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/mixin.js +++ /dev/null
@@ -1,67 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import './boot.js'; - -// unique global id for deduping mixins. -let dedupeId = 0; - -/** - * @constructor - * @extends {Function} - * @private - */ -function MixinFunction(){} -/** @type {(WeakMap | undefined)} */ -MixinFunction.prototype.__mixinApplications; -/** @type {(Object | undefined)} */ -MixinFunction.prototype.__mixinSet; - -/* eslint-disable valid-jsdoc */ -/** - * Wraps an ES6 class expression mixin such that the mixin is only applied - * if it has not already been applied its base argument. Also memoizes mixin - * applications. - * - * @template T - * @param {T} mixin ES6 class expression mixin to wrap - * @return {T} - * @suppress {invalidCasts} - */ -export const dedupingMixin = function(mixin) { - let mixinApplications = /** @type {!MixinFunction} */(mixin).__mixinApplications; - if (!mixinApplications) { - mixinApplications = new WeakMap(); - /** @type {!MixinFunction} */(mixin).__mixinApplications = mixinApplications; - } - // maintain a unique id for each mixin - let mixinDedupeId = dedupeId++; - function dedupingMixin(base) { - let baseSet = /** @type {!MixinFunction} */(base).__mixinSet; - if (baseSet && baseSet[mixinDedupeId]) { - return base; - } - let map = mixinApplications; - let extended = map.get(base); - if (!extended) { - extended = /** @type {!Function} */(mixin)(base); - map.set(base, extended); - } - // copy inherited mixin set from the extended class, or the base class - // NOTE: we avoid use of Set here because some browser (IE11) - // cannot extend a base Set via the constructor. - let mixinSet = Object.create(/** @type {!MixinFunction} */(extended).__mixinSet || baseSet || null); - mixinSet[mixinDedupeId] = true; - /** @type {!MixinFunction} */(extended).__mixinSet = mixinSet; - return extended; - } - - return dedupingMixin; -}; -/* eslint-enable valid-jsdoc */
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/path.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/path.js deleted file mode 100644 index 52fda93..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/path.js +++ /dev/null
@@ -1,255 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import './boot.js'; - -/** - * Module with utilities for manipulating structured data path strings. - * - * @summary Module with utilities for manipulating structured data path strings. - */ - -/** - * Returns true if the given string is a structured data path (has dots). - * - * Example: - * - * ``` - * isPath('foo.bar.baz') // true - * isPath('foo') // false - * ``` - * - * @param {string} path Path string - * @return {boolean} True if the string contained one or more dots - */ -export function isPath(path) { - return path.indexOf('.') >= 0; -} - -/** - * Returns the root property name for the given path. - * - * Example: - * - * ``` - * root('foo.bar.baz') // 'foo' - * root('foo') // 'foo' - * ``` - * - * @param {string} path Path string - * @return {string} Root property name - */ -export function root(path) { - let dotIndex = path.indexOf('.'); - if (dotIndex === -1) { - return path; - } - return path.slice(0, dotIndex); -} - -/** - * Given `base` is `foo.bar`, `foo` is an ancestor, `foo.bar` is not - * Returns true if the given path is an ancestor of the base path. - * - * Example: - * - * ``` - * isAncestor('foo.bar', 'foo') // true - * isAncestor('foo.bar', 'foo.bar') // false - * isAncestor('foo.bar', 'foo.bar.baz') // false - * ``` - * - * @param {string} base Path string to test against. - * @param {string} path Path string to test. - * @return {boolean} True if `path` is an ancestor of `base`. - */ -export function isAncestor(base, path) { - // base.startsWith(path + '.'); - return base.indexOf(path + '.') === 0; -} - -/** - * Given `base` is `foo.bar`, `foo.bar.baz` is an descendant - * - * Example: - * - * ``` - * isDescendant('foo.bar', 'foo.bar.baz') // true - * isDescendant('foo.bar', 'foo.bar') // false - * isDescendant('foo.bar', 'foo') // false - * ``` - * - * @param {string} base Path string to test against. - * @param {string} path Path string to test. - * @return {boolean} True if `path` is a descendant of `base`. - */ -export function isDescendant(base, path) { - // path.startsWith(base + '.'); - return path.indexOf(base + '.') === 0; -} - -/** - * Replaces a previous base path with a new base path, preserving the - * remainder of the path. - * - * User must ensure `path` has a prefix of `base`. - * - * Example: - * - * ``` - * translate('foo.bar', 'zot', 'foo.bar.baz') // 'zot.baz' - * ``` - * - * @param {string} base Current base string to remove - * @param {string} newBase New base string to replace with - * @param {string} path Path to translate - * @return {string} Translated string - */ -export function translate(base, newBase, path) { - return newBase + path.slice(base.length); -} - -/** - * @param {string} base Path string to test against - * @param {string} path Path string to test - * @return {boolean} True if `path` is equal to `base` - */ -export function matches(base, path) { - return (base === path) || - isAncestor(base, path) || - isDescendant(base, path); -} - -/** - * Converts array-based paths to flattened path. String-based paths - * are returned as-is. - * - * Example: - * - * ``` - * normalize(['foo.bar', 0, 'baz']) // 'foo.bar.0.baz' - * normalize('foo.bar.0.baz') // 'foo.bar.0.baz' - * ``` - * - * @param {string | !Array<string|number>} path Input path - * @return {string} Flattened path - */ -export function normalize(path) { - if (Array.isArray(path)) { - let parts = []; - for (let i=0; i<path.length; i++) { - let args = path[i].toString().split('.'); - for (let j=0; j<args.length; j++) { - parts.push(args[j]); - } - } - return parts.join('.'); - } else { - return path; - } -} - -/** - * Splits a path into an array of property names. Accepts either arrays - * of path parts or strings. - * - * Example: - * - * ``` - * split(['foo.bar', 0, 'baz']) // ['foo', 'bar', '0', 'baz'] - * split('foo.bar.0.baz') // ['foo', 'bar', '0', 'baz'] - * ``` - * - * @param {string | !Array<string|number>} path Input path - * @return {!Array<string>} Array of path parts - * @suppress {checkTypes} - */ -export function split(path) { - if (Array.isArray(path)) { - return normalize(path).split('.'); - } - return path.toString().split('.'); -} - -/** - * Reads a value from a path. If any sub-property in the path is `undefined`, - * this method returns `undefined` (will never throw. - * - * @param {Object} root Object from which to dereference path from - * @param {string | !Array<string|number>} path Path to read - * @param {Object=} info If an object is provided to `info`, the normalized - * (flattened) path will be set to `info.path`. - * @return {*} Value at path, or `undefined` if the path could not be - * fully dereferenced. - */ -export function get(root, path, info) { - let prop = root; - let parts = split(path); - // Loop over path parts[0..n-1] and dereference - for (let i=0; i<parts.length; i++) { - if (!prop) { - return; - } - let part = parts[i]; - prop = prop[part]; - } - if (info) { - info.path = parts.join('.'); - } - return prop; -} - -/** - * Sets a value to a path. If any sub-property in the path is `undefined`, - * this method will no-op. - * - * @param {Object} root Object from which to dereference path from - * @param {string | !Array<string|number>} path Path to set - * @param {*} value Value to set to path - * @return {string | undefined} The normalized version of the input path - */ -export function set(root, path, value) { - let prop = root; - let parts = split(path); - let last = parts[parts.length-1]; - if (parts.length > 1) { - // Loop over path parts[0..n-2] and dereference - for (let i=0; i<parts.length-1; i++) { - let part = parts[i]; - prop = prop[part]; - if (!prop) { - return; - } - } - // Set value to object at end of path - prop[last] = value; - } else { - // Simple property set - prop[path] = value; - } - return parts.join('.'); -} - -/** - * Returns true if the given string is a structured data path (has dots). - * - * This function is deprecated. Use `isPath` instead. - * - * Example: - * - * ``` - * isDeep('foo.bar.baz') // true - * isDeep('foo') // false - * ``` - * - * @deprecated - * @param {string} path Path string - * @return {boolean} True if the string contained one or more dots - */ -export const isDeep = isPath;
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/render-status.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/render-status.js deleted file mode 100644 index 0132699a..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/render-status.js +++ /dev/null
@@ -1,118 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -/** - * Module for scheduling flushable pre-render and post-render tasks. - * - * @summary Module for scheduling flushable pre-render and post-render tasks. - */ - -import './boot.js'; - -let scheduled = false; -let beforeRenderQueue = []; -let afterRenderQueue = []; - -function schedule() { - scheduled = true; - // before next render - requestAnimationFrame(function() { - scheduled = false; - flushQueue(beforeRenderQueue); - // after the render - setTimeout(function() { - runQueue(afterRenderQueue); - }); - }); -} - -function flushQueue(queue) { - while (queue.length) { - callMethod(queue.shift()); - } -} - -function runQueue(queue) { - for (let i=0, l=queue.length; i < l; i++) { - callMethod(queue.shift()); - } -} - -function callMethod(info) { - const context = info[0]; - const callback = info[1]; - const args = info[2]; - try { - callback.apply(context, args); - } catch(e) { - setTimeout(() => { - throw e; - }); - } -} - -/** - * Flushes all `beforeNextRender` tasks, followed by all `afterNextRender` - * tasks. - * - * @return {void} - */ -export function flush() { - while (beforeRenderQueue.length || afterRenderQueue.length) { - flushQueue(beforeRenderQueue); - flushQueue(afterRenderQueue); - } - scheduled = false; -} - - -/** - * Enqueues a callback which will be run before the next render, at - * `requestAnimationFrame` timing. - * - * This method is useful for enqueuing work that requires DOM measurement, - * since measurement may not be reliable in custom element callbacks before - * the first render, as well as for batching measurement tasks in general. - * - * Tasks in this queue may be flushed by calling `flush()`. - * - * @param {*} context Context object the callback function will be bound to - * @param {function(...*):void} callback Callback function - * @param {!Array=} args An array of arguments to call the callback function with - * @return {void} - */ -export function beforeNextRender(context, callback, args) { - if (!scheduled) { - schedule(); - } - beforeRenderQueue.push([context, callback, args]); -} - -/** - * Enqueues a callback which will be run after the next render, equivalent - * to one task (`setTimeout`) after the next `requestAnimationFrame`. - * - * This method is useful for tuning the first-render performance of an - * element or application by deferring non-critical work until after the - * first paint. Typical non-render-critical work may include adding UI - * event listeners and aria attributes. - * - * @param {*} context Context object the callback function will be bound to - * @param {function(...*):void} callback Callback function - * @param {!Array=} args An array of arguments to call the callback function with - * @return {void} - */ -export function afterNextRender(context, callback, args) { - if (!scheduled) { - schedule(); - } - afterRenderQueue.push([context, callback, args]); -} -
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/resolve-url.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/resolve-url.js deleted file mode 100644 index 06b2292e..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/resolve-url.js +++ /dev/null
@@ -1,87 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import './boot.js'; - -let CSS_URL_RX = /(url\()([^)]*)(\))/g; -let ABS_URL = /(^\/)|(^#)|(^[\w-\d]*:)/; -let workingURL; -let resolveDoc; -/** - * Resolves the given URL against the provided `baseUri'. - * - * Note that this function performs no resolution for URLs that start - * with `/` (absolute URLs) or `#` (hash identifiers). For general purpose - * URL resolution, use `window.URL`. - * - * @param {string} url Input URL to resolve - * @param {?string=} baseURI Base URI to resolve the URL against - * @return {string} resolved URL - */ -export function resolveUrl(url, baseURI) { - if (url && ABS_URL.test(url)) { - return url; - } - // Lazy feature detection. - if (workingURL === undefined) { - workingURL = false; - try { - const u = new URL('b', 'http://a'); - u.pathname = 'c%20d'; - workingURL = (u.href === 'http://a/c%20d'); - } catch (e) { - // silently fail - } - } - if (!baseURI) { - baseURI = document.baseURI || window.location.href; - } - if (workingURL) { - return (new URL(url, baseURI)).href; - } - // Fallback to creating an anchor into a disconnected document. - if (!resolveDoc) { - resolveDoc = document.implementation.createHTMLDocument('temp'); - resolveDoc.base = resolveDoc.createElement('base'); - resolveDoc.head.appendChild(resolveDoc.base); - resolveDoc.anchor = resolveDoc.createElement('a'); - resolveDoc.body.appendChild(resolveDoc.anchor); - } - resolveDoc.base.href = baseURI; - resolveDoc.anchor.href = url; - return resolveDoc.anchor.href || url; - -} - -/** - * Resolves any relative URL's in the given CSS text against the provided - * `ownerDocument`'s `baseURI`. - * - * @param {string} cssText CSS text to process - * @param {string} baseURI Base URI to resolve the URL against - * @return {string} Processed CSS text with resolved URL's - */ -export function resolveCss(cssText, baseURI) { - return cssText.replace(CSS_URL_RX, function(m, pre, url, post) { - return pre + '\'' + - resolveUrl(url.replace(/["']/g, ''), baseURI) + - '\'' + post; - }); -} - -/** - * Returns a path from a given `url`. The path includes the trailing - * `/` from the url. - * - * @param {string} url Input URL to transform - * @return {string} resolved path - */ -export function pathFromUrl(url) { - return url.substring(0, url.lastIndexOf('/') + 1); -}
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/settings.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/settings.js deleted file mode 100644 index 6b878c4e..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/settings.js +++ /dev/null
@@ -1,160 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -import './boot.js'; - -import { pathFromUrl } from './resolve-url.js'; -export const useShadow = !(window.ShadyDOM); -export const useNativeCSSProperties = Boolean(!window.ShadyCSS || window.ShadyCSS.nativeCss); -export const useNativeCustomElements = !(window.customElements.polyfillWrapFlushCallback); - - -/** - * Globally settable property that is automatically assigned to - * `ElementMixin` instances, useful for binding in templates to - * make URL's relative to an application's root. Defaults to the main - * document URL, but can be overridden by users. It may be useful to set - * `rootPath` to provide a stable application mount path when - * using client side routing. - */ -export let rootPath = pathFromUrl(document.baseURI || window.location.href); - -/** - * Sets the global rootPath property used by `ElementMixin` and - * available via `rootPath`. - * - * @param {string} path The new root path - * @return {void} - */ -export const setRootPath = function(path) { - rootPath = path; -}; - -/** - * A global callback used to sanitize any value before inserting it into the DOM. - * The callback signature is: - * - * function sanitizeDOMValue(value, name, type, node) { ... } - * - * Where: - * - * `value` is the value to sanitize. - * `name` is the name of an attribute or property (for example, href). - * `type` indicates where the value is being inserted: one of property, attribute, or text. - * `node` is the node where the value is being inserted. - * - * @type {(function(*,string,string,Node):*)|undefined} - */ -export let sanitizeDOMValue = window.Polymer && window.Polymer.sanitizeDOMValue || undefined; - -/** - * Sets the global sanitizeDOMValue available via this module's exported - * `sanitizeDOMValue` variable. - * - * @param {(function(*,string,string,Node):*)|undefined} newSanitizeDOMValue the global sanitizeDOMValue callback - * @return {void} - */ -export const setSanitizeDOMValue = function(newSanitizeDOMValue) { - sanitizeDOMValue = newSanitizeDOMValue; -}; - -/** - * Globally settable property to make Polymer Gestures use passive TouchEvent listeners when recognizing gestures. - * When set to `true`, gestures made from touch will not be able to prevent scrolling, allowing for smoother - * scrolling performance. - * Defaults to `false` for backwards compatibility. - */ -export let passiveTouchGestures = false; - -/** - * Sets `passiveTouchGestures` globally for all elements using Polymer Gestures. - * - * @param {boolean} usePassive enable or disable passive touch gestures globally - * @return {void} - */ -export const setPassiveTouchGestures = function(usePassive) { - passiveTouchGestures = usePassive; -}; - -/** - * Setting to ensure Polymer template evaluation only occurs based on tempates - * defined in trusted script. When true, `<dom-module>` re-registration is - * disallowed, `<dom-bind>` is disabled, and `<dom-if>`/`<dom-repeat>` - * templates will only evaluate in the context of a trusted element template. - */ -export let strictTemplatePolicy = false; - -/** - * Sets `strictTemplatePolicy` globally for all elements - * - * @param {boolean} useStrictPolicy enable or disable strict template policy - * globally - * @return {void} - */ -export const setStrictTemplatePolicy = function(useStrictPolicy) { - strictTemplatePolicy = useStrictPolicy; -}; - -/** - * Setting to enable dom-module lookup from Polymer.Element. By default, - * templates must be defined in script using the `static get template()` - * getter and the `html` tag function. To enable legacy loading of templates - * via dom-module, set this flag to true. - */ -export let allowTemplateFromDomModule = false; - -/** - * Sets `lookupTemplateFromDomModule` globally for all elements - * - * @param {boolean} allowDomModule enable or disable template lookup - * globally - * @return {void} - */ -export const setAllowTemplateFromDomModule = function(allowDomModule) { - allowTemplateFromDomModule = allowDomModule; -}; - -/** - * Setting to skip processing style includes and re-writing urls in css styles. - * Normally "included" styles are pulled into the element and all urls in styles - * are re-written to be relative to the containing script url. - * If no includes or relative urls are used in styles, these steps can be - * skipped as an optimization. - */ -export let legacyOptimizations = false; - -/** - * Sets `legacyOptimizations` globally for all elements to enable optimizations - * when only legacy based elements are used. - * - * @param {boolean} useLegacyOptimizations enable or disable legacy optimizations - * includes and url rewriting - * @return {void} - */ -export const setLegacyOptimizations = function(useLegacyOptimizations) { - legacyOptimizations = useLegacyOptimizations; -}; - -/** - * Setting to perform initial rendering synchronously when running under ShadyDOM. - * This matches the behavior of Polymer 1. - */ -export let syncInitialRender = false; - -/** - * Sets `syncInitialRender` globally for all elements to enable synchronous - * initial rendering. - * - * @param {boolean} useSyncInitialRender enable or disable synchronous initial - * rendering globally. - * @return {void} - */ -export const setSyncInitialRender = function(useSyncInitialRender) { - syncInitialRender = useSyncInitialRender; -};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/style-gather.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/style-gather.js deleted file mode 100644 index b67fcaf..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/style-gather.js +++ /dev/null
@@ -1,274 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -/** - * Module with utilities for collection CSS text from `<templates>`, external - * stylesheets, and `dom-module`s. - * - * @summary Module with utilities for collection CSS text from various sources. - */ - -import { DomModule } from '../elements/dom-module.js'; -import { resolveCss } from './resolve-url.js'; - -const MODULE_STYLE_LINK_SELECTOR = 'link[rel=import][type~=css]'; -const INCLUDE_ATTR = 'include'; -const SHADY_UNSCOPED_ATTR = 'shady-unscoped'; - -/** - * @param {string} moduleId . - * @return {?DomModule} . - */ -function importModule(moduleId) { - return /** @type {?DomModule} */(DomModule.import(moduleId)); -} - -function styleForImport(importDoc) { - // NOTE: polyfill affordance. - // under the HTMLImports polyfill, there will be no 'body', - // but the import pseudo-doc can be used directly. - let container = importDoc.body ? importDoc.body : importDoc; - const importCss = resolveCss(container.textContent, - importDoc.baseURI); - const style = document.createElement('style'); - style.textContent = importCss; - return style; -} - -/** @typedef {{assetpath: string}} */ -let templateWithAssetPath; // eslint-disable-line no-unused-vars - - -/** - * Returns a list of <style> elements in a space-separated list of `dom-module`s. - * - * @function - * @param {string} moduleIds List of dom-module id's within which to - * search for css. - * @return {!Array<!HTMLStyleElement>} Array of contained <style> elements - */ -export function stylesFromModules(moduleIds) { - const modules = moduleIds.trim().split(/\s+/); - const styles = []; - for (let i=0; i < modules.length; i++) { - styles.push(...stylesFromModule(modules[i])); - } - return styles; -} - -/** - * Returns a list of <style> elements in a given `dom-module`. - * Styles in a `dom-module` can come either from `<style>`s within the - * first `<template>`, or else from one or more - * `<link rel="import" type="css">` links outside the template. - * - * @param {string} moduleId dom-module id to gather styles from - * @return {!Array<!HTMLStyleElement>} Array of contained styles. - */ -export function stylesFromModule(moduleId) { - const m = importModule(moduleId); - - if (!m) { - console.warn('Could not find style data in module named', moduleId); - return []; - } - - if (m._styles === undefined) { - const styles = []; - // module imports: <link rel="import" type="css"> - styles.push(..._stylesFromModuleImports(m)); - // include css from the first template in the module - const template = /** @type {?HTMLTemplateElement} */( - m.querySelector('template')); - if (template) { - styles.push(...stylesFromTemplate(template, - /** @type {templateWithAssetPath} */(m).assetpath)); - } - - m._styles = styles; - } - - return m._styles; -} - -/** - * Returns the `<style>` elements within a given template. - * - * @param {!HTMLTemplateElement} template Template to gather styles from - * @param {string=} baseURI baseURI for style content - * @return {!Array<!HTMLStyleElement>} Array of styles - */ -export function stylesFromTemplate(template, baseURI) { - if (!template._styles) { - const styles = []; - // if element is a template, get content from its .content - const e$ = template.content.querySelectorAll('style'); - for (let i=0; i < e$.length; i++) { - let e = e$[i]; - // support style sharing by allowing styles to "include" - // other dom-modules that contain styling - let include = e.getAttribute(INCLUDE_ATTR); - if (include) { - styles.push(...stylesFromModules(include).filter(function(item, index, self) { - return self.indexOf(item) === index; - })); - } - if (baseURI) { - e.textContent = - resolveCss(e.textContent, /** @type {string} */ (baseURI)); - } - styles.push(e); - } - template._styles = styles; - } - return template._styles; -} - -/** - * Returns a list of <style> elements from stylesheets loaded via `<link rel="import" type="css">` links within the specified `dom-module`. - * - * @param {string} moduleId Id of `dom-module` to gather CSS from - * @return {!Array<!HTMLStyleElement>} Array of contained styles. - */ -export function stylesFromModuleImports(moduleId) { - let m = importModule(moduleId); - return m ? _stylesFromModuleImports(m) : []; -} - -/** - * @param {!HTMLElement} module dom-module element that could contain `<link rel="import" type="css">` styles - * @return {!Array<!HTMLStyleElement>} Array of contained styles - */ -function _stylesFromModuleImports(module) { - const styles = []; - const p$ = module.querySelectorAll(MODULE_STYLE_LINK_SELECTOR); - for (let i=0; i < p$.length; i++) { - let p = p$[i]; - if (p.import) { - const importDoc = p.import; - const unscoped = p.hasAttribute(SHADY_UNSCOPED_ATTR); - if (unscoped && !importDoc._unscopedStyle) { - const style = styleForImport(importDoc); - style.setAttribute(SHADY_UNSCOPED_ATTR, ''); - importDoc._unscopedStyle = style; - } else if (!importDoc._style) { - importDoc._style = styleForImport(importDoc); - } - styles.push(unscoped ? importDoc._unscopedStyle : importDoc._style); - } - } - return styles; -} - -/** - * - * Returns CSS text of styles in a space-separated list of `dom-module`s. - * Note: This method is deprecated, use `stylesFromModules` instead. - * - * @deprecated - * @param {string} moduleIds List of dom-module id's within which to - * search for css. - * @return {string} Concatenated CSS content from specified `dom-module`s - */ -export function cssFromModules(moduleIds) { - let modules = moduleIds.trim().split(/\s+/); - let cssText = ''; - for (let i=0; i < modules.length; i++) { - cssText += cssFromModule(modules[i]); - } - return cssText; -} - -/** - * Returns CSS text of styles in a given `dom-module`. CSS in a `dom-module` - * can come either from `<style>`s within the first `<template>`, or else - * from one or more `<link rel="import" type="css">` links outside the - * template. - * - * Any `<styles>` processed are removed from their original location. - * Note: This method is deprecated, use `styleFromModule` instead. - * - * @deprecated - * @param {string} moduleId dom-module id to gather styles from - * @return {string} Concatenated CSS content from specified `dom-module` - */ -export function cssFromModule(moduleId) { - let m = importModule(moduleId); - if (m && m._cssText === undefined) { - // module imports: <link rel="import" type="css"> - let cssText = _cssFromModuleImports(m); - // include css from the first template in the module - let t = /** @type {?HTMLTemplateElement} */(m.querySelector('template')); - if (t) { - cssText += cssFromTemplate(t, - /** @type {templateWithAssetPath} */(m).assetpath); - } - m._cssText = cssText || null; - } - if (!m) { - console.warn('Could not find style data in module named', moduleId); - } - return m && m._cssText || ''; -} - -/** - * Returns CSS text of `<styles>` within a given template. - * - * Any `<styles>` processed are removed from their original location. - * Note: This method is deprecated, use `styleFromTemplate` instead. - * - * @deprecated - * @param {!HTMLTemplateElement} template Template to gather styles from - * @param {string} baseURI Base URI to resolve the URL against - * @return {string} Concatenated CSS content from specified template - */ -export function cssFromTemplate(template, baseURI) { - let cssText = ''; - const e$ = stylesFromTemplate(template, baseURI); - // if element is a template, get content from its .content - for (let i=0; i < e$.length; i++) { - let e = e$[i]; - if (e.parentNode) { - e.parentNode.removeChild(e); - } - cssText += e.textContent; - } - return cssText; -} - -/** - * Returns CSS text from stylesheets loaded via `<link rel="import" type="css">` - * links within the specified `dom-module`. - * - * Note: This method is deprecated, use `stylesFromModuleImports` instead. - * - * @deprecated - * - * @param {string} moduleId Id of `dom-module` to gather CSS from - * @return {string} Concatenated CSS content from links in specified `dom-module` - */ -export function cssFromModuleImports(moduleId) { - let m = importModule(moduleId); - return m ? _cssFromModuleImports(m) : ''; -} - -/** - * @deprecated - * @param {!HTMLElement} module dom-module element that could contain `<link rel="import" type="css">` styles - * @return {string} Concatenated CSS content from links in the dom-module - */ -function _cssFromModuleImports(module) { - let cssText = ''; - let styles = _stylesFromModuleImports(module); - for (let i=0; i < styles.length; i++) { - cssText += styles[i].textContent; - } - return cssText; -}
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/telemetry.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/telemetry.js deleted file mode 100644 index 5e12c3e..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/telemetry.js +++ /dev/null
@@ -1,50 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -/** - * Total number of Polymer element instances created. - * @type {number} - */ -export let instanceCount = 0; - -export function incrementInstanceCount() { - instanceCount++; -} - -/** - * Array of Polymer element classes that have been finalized. - * @type {!Array<!PolymerElementConstructor>} - */ -export const registrations = []; - -/** - * @param {!PolymerElementConstructor} prototype Element prototype to log - * @private - */ -function _regLog(prototype) { - console.log('[' + /** @type {?} */(prototype).is + ']: registered'); -} - -/** - * Registers a class prototype for telemetry purposes. - * @param {!PolymerElementConstructor} prototype Element prototype to register - * @protected - */ -export function register(prototype) { - registrations.push(prototype); -} - -/** - * Logs all elements registered with an `is` to the console. - * @public - */ -export function dumpRegistrations() { - registrations.forEach(_regLog); -} \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/templatize.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/templatize.js deleted file mode 100644 index 9f05135..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/templatize.js +++ /dev/null
@@ -1,610 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -/** - * Module for preparing and stamping instances of templates that utilize - * Polymer's data-binding and declarative event listener features. - * - * Example: - * - * // Get a template from somewhere, e.g. light DOM - * let template = this.querySelector('template'); - * // Prepare the template - * let TemplateClass = Templatize.templatize(template); - * // Instance the template with an initial data model - * let instance = new TemplateClass({myProp: 'initial'}); - * // Insert the instance's DOM somewhere, e.g. element's shadow DOM - * this.shadowRoot.appendChild(instance.root); - * // Changing a property on the instance will propagate to bindings - * // in the template - * instance.myProp = 'new value'; - * - * The `options` dictionary passed to `templatize` allows for customizing - * features of the generated template class, including how outer-scope host - * properties should be forwarded into template instances, how any instance - * properties added into the template's scope should be notified out to - * the host, and whether the instance should be decorated as a "parent model" - * of any event handlers. - * - * // Customize property forwarding and event model decoration - * let TemplateClass = Templatize.templatize(template, this, { - * parentModel: true, - * forwardHostProp(property, value) {...}, - * instanceProps: {...}, - * notifyInstanceProp(instance, property, value) {...}, - * }); - * - * @summary Module for preparing and stamping instances of templates - * utilizing Polymer templating features. - */ - -import './boot.js'; - -import { PropertyEffects } from '../mixins/property-effects.js'; -import { MutableData } from '../mixins/mutable-data.js'; -import { strictTemplatePolicy } from './settings.js'; -import { wrap } from './wrap.js'; - -// Base class for HTMLTemplateElement extension that has property effects -// machinery for propagating host properties to children. This is an ES5 -// class only because Babel (incorrectly) requires super() in the class -// constructor even though no `this` is used and it returns an instance. -let newInstance = null; - -/** - * @constructor - * @extends {HTMLTemplateElement} - * @private - */ -function HTMLTemplateElementExtension() { return newInstance; } -HTMLTemplateElementExtension.prototype = Object.create(HTMLTemplateElement.prototype, { - constructor: { - value: HTMLTemplateElementExtension, - writable: true - } -}); - -/** - * @constructor - * @implements {Polymer_PropertyEffects} - * @extends {HTMLTemplateElementExtension} - * @private - */ -const DataTemplate = PropertyEffects(HTMLTemplateElementExtension); - -/** - * @constructor - * @implements {Polymer_MutableData} - * @extends {DataTemplate} - * @private - */ -const MutableDataTemplate = MutableData(DataTemplate); - -// Applies a DataTemplate subclass to a <template> instance -function upgradeTemplate(template, constructor) { - newInstance = template; - Object.setPrototypeOf(template, constructor.prototype); - new constructor(); - newInstance = null; -} - -/** - * Base class for TemplateInstance. - * @constructor - * @implements {Polymer_PropertyEffects} - * @private - */ -const templateInstanceBase = PropertyEffects(class {}); - -/** - * @polymer - * @customElement - * @appliesMixin PropertyEffects - * @unrestricted - */ -class TemplateInstanceBase extends templateInstanceBase { - constructor(props) { - super(); - this._configureProperties(props); - /** @type {!StampedTemplate} */ - this.root = this._stampTemplate(this.__dataHost); - // Save list of stamped children - let children = this.children = []; - // Polymer 1.x did not use `Polymer.dom` here so not bothering. - for (let n = this.root.firstChild; n; n=n.nextSibling) { - children.push(n); - n.__templatizeInstance = this; - } - if (this.__templatizeOwner && - this.__templatizeOwner.__hideTemplateChildren__) { - this._showHideChildren(true); - } - // Flush props only when props are passed if instance props exist - // or when there isn't instance props. - let options = this.__templatizeOptions; - if ((props && options.instanceProps) || !options.instanceProps) { - this._enableProperties(); - } - } - /** - * Configure the given `props` by calling `_setPendingProperty`. Also - * sets any properties stored in `__hostProps`. - * @private - * @param {Object} props Object of property name-value pairs to set. - * @return {void} - */ - _configureProperties(props) { - let options = this.__templatizeOptions; - if (options.forwardHostProp) { - for (let hprop in this.__hostProps) { - this._setPendingProperty(hprop, this.__dataHost['_host_' + hprop]); - } - } - // Any instance props passed in the constructor will overwrite host props; - // normally this would be a user error but we don't specifically filter them - for (let iprop in props) { - this._setPendingProperty(iprop, props[iprop]); - } - } - /** - * Forwards a host property to this instance. This method should be - * called on instances from the `options.forwardHostProp` callback - * to propagate changes of host properties to each instance. - * - * Note this method enqueues the change, which are flushed as a batch. - * - * @param {string} prop Property or path name - * @param {*} value Value of the property to forward - * @return {void} - */ - forwardHostProp(prop, value) { - if (this._setPendingPropertyOrPath(prop, value, false, true)) { - this.__dataHost._enqueueClient(this); - } - } - - /** - * Override point for adding custom or simulated event handling. - * - * @override - * @param {!Node} node Node to add event listener to - * @param {string} eventName Name of event - * @param {function(!Event):void} handler Listener function to add - * @return {void} - */ - _addEventListenerToNode(node, eventName, handler) { - if (this._methodHost && this.__templatizeOptions.parentModel) { - // If this instance should be considered a parent model, decorate - // events this template instance as `model` - this._methodHost._addEventListenerToNode(node, eventName, (e) => { - e.model = this; - handler(e); - }); - } else { - // Otherwise delegate to the template's host (which could be) - // another template instance - let templateHost = this.__dataHost.__dataHost; - if (templateHost) { - templateHost._addEventListenerToNode(node, eventName, handler); - } - } - } - /** - * Shows or hides the template instance top level child elements. For - * text nodes, `textContent` is removed while "hidden" and replaced when - * "shown." - * @param {boolean} hide Set to true to hide the children; - * set to false to show them. - * @return {void} - * @protected - */ - _showHideChildren(hide) { - let c = this.children; - for (let i=0; i<c.length; i++) { - let n = c[i]; - // Ignore non-changes - if (Boolean(hide) != Boolean(n.__hideTemplateChildren__)) { - if (n.nodeType === Node.TEXT_NODE) { - if (hide) { - n.__polymerTextContent__ = n.textContent; - n.textContent = ''; - } else { - n.textContent = n.__polymerTextContent__; - } - // remove and replace slot - } else if (n.localName === 'slot') { - if (hide) { - n.__polymerReplaced__ = document.createComment('hidden-slot'); - wrap(wrap(n).parentNode).replaceChild(n.__polymerReplaced__, n); - } else { - const replace = n.__polymerReplaced__; - if (replace) { - wrap(wrap(replace).parentNode).replaceChild(n, replace); - } - } - } - - else if (n.style) { - if (hide) { - n.__polymerDisplay__ = n.style.display; - n.style.display = 'none'; - } else { - n.style.display = n.__polymerDisplay__; - } - } - } - n.__hideTemplateChildren__ = hide; - if (n._showHideChildren) { - n._showHideChildren(hide); - } - } - } - /** - * Overrides default property-effects implementation to intercept - * textContent bindings while children are "hidden" and cache in - * private storage for later retrieval. - * - * @override - * @param {!Node} node The node to set a property on - * @param {string} prop The property to set - * @param {*} value The value to set - * @return {void} - * @protected - */ - _setUnmanagedPropertyToNode(node, prop, value) { - if (node.__hideTemplateChildren__ && - node.nodeType == Node.TEXT_NODE && prop == 'textContent') { - node.__polymerTextContent__ = value; - } else { - super._setUnmanagedPropertyToNode(node, prop, value); - } - } - /** - * Find the parent model of this template instance. The parent model - * is either another templatize instance that had option `parentModel: true`, - * or else the host element. - * - * @return {!Polymer_PropertyEffects} The parent model of this instance - */ - get parentModel() { - let model = this.__parentModel; - if (!model) { - let options; - model = this; - do { - // A template instance's `__dataHost` is a <template> - // `model.__dataHost.__dataHost` is the template's host - model = model.__dataHost.__dataHost; - } while ((options = model.__templatizeOptions) && !options.parentModel); - this.__parentModel = model; - } - return model; - } - - /** - * Stub of HTMLElement's `dispatchEvent`, so that effects that may - * dispatch events safely no-op. - * - * @param {Event} event Event to dispatch - * @return {boolean} Always true. - */ - dispatchEvent(event) { // eslint-disable-line no-unused-vars - return true; - } -} - -/** @type {!DataTemplate} */ -TemplateInstanceBase.prototype.__dataHost; -/** @type {!TemplatizeOptions} */ -TemplateInstanceBase.prototype.__templatizeOptions; -/** @type {!Polymer_PropertyEffects} */ -TemplateInstanceBase.prototype._methodHost; -/** @type {!Object} */ -TemplateInstanceBase.prototype.__templatizeOwner; -/** @type {!Object} */ -TemplateInstanceBase.prototype.__hostProps; - -/** - * @constructor - * @extends {TemplateInstanceBase} - * @implements {Polymer_MutableData} - * @private - */ -const MutableTemplateInstanceBase = MutableData(TemplateInstanceBase); - -function findMethodHost(template) { - // Technically this should be the owner of the outermost template. - // In shadow dom, this is always getRootNode().host, but we can - // approximate this via cooperation with our dataHost always setting - // `_methodHost` as long as there were bindings (or id's) on this - // instance causing it to get a dataHost. - let templateHost = template.__dataHost; - return templateHost && templateHost._methodHost || templateHost; -} - -/* eslint-disable valid-jsdoc */ -/** - * @suppress {missingProperties} class.prototype is not defined for some reason - */ -function createTemplatizerClass(template, templateInfo, options) { - /** - * @constructor - * @extends {TemplateInstanceBase} - */ - let templatizerBase = options.mutableData ? - MutableTemplateInstanceBase : TemplateInstanceBase; - - // Affordance for global mixins onto TemplatizeInstance - if (templatize.mixin) { - templatizerBase = templatize.mixin(templatizerBase); - } - - /** - * Anonymous class created by the templatize - * @constructor - * @private - */ - let klass = class extends templatizerBase { }; - /** @override */ - klass.prototype.__templatizeOptions = options; - klass.prototype._bindTemplate(template); - addNotifyEffects(klass, template, templateInfo, options); - return klass; -} - -/** - * @suppress {missingProperties} class.prototype is not defined for some reason - */ -function addPropagateEffects(template, templateInfo, options) { - let userForwardHostProp = options.forwardHostProp; - if (userForwardHostProp) { - // Provide data API and property effects on memoized template class - let klass = templateInfo.templatizeTemplateClass; - if (!klass) { - /** - * @constructor - * @extends {DataTemplate} - */ - let templatizedBase = options.mutableData ? MutableDataTemplate : DataTemplate; - /** @private */ - klass = templateInfo.templatizeTemplateClass = - class TemplatizedTemplate extends templatizedBase {}; - // Add template - >instances effects - // and host <- template effects - let hostProps = templateInfo.hostProps; - for (let prop in hostProps) { - klass.prototype._addPropertyEffect('_host_' + prop, - klass.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE, - {fn: createForwardHostPropEffect(prop, userForwardHostProp)}); - klass.prototype._createNotifyingProperty('_host_' + prop); - } - } - upgradeTemplate(template, klass); - // Mix any pre-bound data into __data; no need to flush this to - // instances since they pull from the template at instance-time - if (template.__dataProto) { - // Note, generally `__dataProto` could be chained, but it's guaranteed - // to not be since this is a vanilla template we just added effects to - Object.assign(template.__data, template.__dataProto); - } - // Clear any pending data for performance - template.__dataTemp = {}; - template.__dataPending = null; - template.__dataOld = null; - template._enableProperties(); - } -} -/* eslint-enable valid-jsdoc */ - -function createForwardHostPropEffect(hostProp, userForwardHostProp) { - return function forwardHostProp(template, prop, props) { - userForwardHostProp.call(template.__templatizeOwner, - prop.substring('_host_'.length), props[prop]); - }; -} - -function addNotifyEffects(klass, template, templateInfo, options) { - let hostProps = templateInfo.hostProps || {}; - for (let iprop in options.instanceProps) { - delete hostProps[iprop]; - let userNotifyInstanceProp = options.notifyInstanceProp; - if (userNotifyInstanceProp) { - klass.prototype._addPropertyEffect(iprop, - klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY, - {fn: createNotifyInstancePropEffect(iprop, userNotifyInstanceProp)}); - } - } - if (options.forwardHostProp && template.__dataHost) { - for (let hprop in hostProps) { - klass.prototype._addPropertyEffect(hprop, - klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY, - {fn: createNotifyHostPropEffect()}); - } - } -} - -function createNotifyInstancePropEffect(instProp, userNotifyInstanceProp) { - return function notifyInstanceProp(inst, prop, props) { - userNotifyInstanceProp.call(inst.__templatizeOwner, - inst, prop, props[prop]); - }; -} - -function createNotifyHostPropEffect() { - return function notifyHostProp(inst, prop, props) { - inst.__dataHost._setPendingPropertyOrPath('_host_' + prop, props[prop], true, true); - }; -} - - -/** - * Returns an anonymous `PropertyEffects` class bound to the - * `<template>` provided. Instancing the class will result in the - * template being stamped into a document fragment stored as the instance's - * `root` property, after which it can be appended to the DOM. - * - * Templates may utilize all Polymer data-binding features as well as - * declarative event listeners. Event listeners and inline computing - * functions in the template will be called on the host of the template. - * - * The constructor returned takes a single argument dictionary of initial - * property values to propagate into template bindings. Additionally - * host properties can be forwarded in, and instance properties can be - * notified out by providing optional callbacks in the `options` dictionary. - * - * Valid configuration in `options` are as follows: - * - * - `forwardHostProp(property, value)`: Called when a property referenced - * in the template changed on the template's host. As this library does - * not retain references to templates instanced by the user, it is the - * templatize owner's responsibility to forward host property changes into - * user-stamped instances. The `instance.forwardHostProp(property, value)` - * method on the generated class should be called to forward host - * properties into the template to prevent unnecessary property-changed - * notifications. Any properties referenced in the template that are not - * defined in `instanceProps` will be notified up to the template's host - * automatically. - * - `instanceProps`: Dictionary of property names that will be added - * to the instance by the templatize owner. These properties shadow any - * host properties, and changes within the template to these properties - * will result in `notifyInstanceProp` being called. - * - `mutableData`: When `true`, the generated class will skip strict - * dirty-checking for objects and arrays (always consider them to be - * "dirty"). - * - `notifyInstanceProp(instance, property, value)`: Called when - * an instance property changes. Users may choose to call `notifyPath` - * on e.g. the owner to notify the change. - * - `parentModel`: When `true`, events handled by declarative event listeners - * (`on-event="handler"`) will be decorated with a `model` property pointing - * to the template instance that stamped it. It will also be returned - * from `instance.parentModel` in cases where template instance nesting - * causes an inner model to shadow an outer model. - * - * All callbacks are called bound to the `owner`. Any context - * needed for the callbacks (such as references to `instances` stamped) - * should be stored on the `owner` such that they can be retrieved via - * `this`. - * - * When `options.forwardHostProp` is declared as an option, any properties - * referenced in the template will be automatically forwarded from the host of - * the `<template>` to instances, with the exception of any properties listed in - * the `options.instanceProps` object. `instanceProps` are assumed to be - * managed by the owner of the instances, either passed into the constructor - * or set after the fact. Note, any properties passed into the constructor will - * always be set to the instance (regardless of whether they would normally - * be forwarded from the host). - * - * Note that `templatize()` can be run only once for a given `<template>`. - * Further calls will result in an error. Also, there is a special - * behavior if the template was duplicated through a mechanism such as - * `<dom-repeat>` or `<test-fixture>`. In this case, all calls to - * `templatize()` return the same class for all duplicates of a template. - * The class returned from `templatize()` is generated only once using - * the `options` from the first call. This means that any `options` - * provided to subsequent calls will be ignored. Therefore, it is very - * important not to close over any variables inside the callbacks. Also, - * arrow functions must be avoided because they bind the outer `this`. - * Inside the callbacks, any contextual information can be accessed - * through `this`, which points to the `owner`. - * - * @param {!HTMLTemplateElement} template Template to templatize - * @param {Polymer_PropertyEffects=} owner Owner of the template instances; - * any optional callbacks will be bound to this owner. - * @param {Object=} options Options dictionary (see summary for details) - * @return {function(new:TemplateInstanceBase)} Generated class bound to the template - * provided - * @suppress {invalidCasts} - */ -export function templatize(template, owner, options) { - // Under strictTemplatePolicy, the templatized element must be owned - // by a (trusted) Polymer element, indicated by existence of _methodHost; - // e.g. for dom-if & dom-repeat in main document, _methodHost is null - if (strictTemplatePolicy && !findMethodHost(template)) { - throw new Error('strictTemplatePolicy: template owner not trusted'); - } - options = /** @type {!TemplatizeOptions} */(options || {}); - if (template.__templatizeOwner) { - throw new Error('A <template> can only be templatized once'); - } - template.__templatizeOwner = owner; - const ctor = owner ? owner.constructor : TemplateInstanceBase; - let templateInfo = ctor._parseTemplate(template); - // Get memoized base class for the prototypical template, which - // includes property effects for binding template & forwarding - /** - * @constructor - * @extends {TemplateInstanceBase} - */ - let baseClass = templateInfo.templatizeInstanceClass; - if (!baseClass) { - baseClass = createTemplatizerClass(template, templateInfo, options); - templateInfo.templatizeInstanceClass = baseClass; - } - // Host property forwarding must be installed onto template instance - addPropagateEffects(template, templateInfo, options); - // Subclass base class and add reference for this specific template - /** @private */ - let klass = class TemplateInstance extends baseClass {}; - /** @override */ - klass.prototype._methodHost = findMethodHost(template); - /** @override */ - klass.prototype.__dataHost = /** @type {!DataTemplate} */ (template); - /** @override */ - klass.prototype.__templatizeOwner = /** @type {!Object} */ (owner); - /** @override */ - klass.prototype.__hostProps = templateInfo.hostProps; - klass = /** @type {function(new:TemplateInstanceBase)} */(klass); //eslint-disable-line no-self-assign - return klass; -} - -/** - * Returns the template "model" associated with a given element, which - * serves as the binding scope for the template instance the element is - * contained in. A template model is an instance of - * `TemplateInstanceBase`, and should be used to manipulate data - * associated with this template instance. - * - * Example: - * - * let model = modelForElement(el); - * if (model.index < 10) { - * model.set('item.checked', true); - * } - * - * @param {HTMLTemplateElement} template The model will be returned for - * elements stamped from this template - * @param {Node=} node Node for which to return a template model. - * @return {TemplateInstanceBase} Template instance representing the - * binding scope for the element - */ -export function modelForElement(template, node) { - let model; - while (node) { - // An element with a __templatizeInstance marks the top boundary - // of a scope; walk up until we find one, and then ensure that - // its __dataHost matches `this`, meaning this dom-repeat stamped it - if ((model = node.__templatizeInstance)) { - // Found an element stamped by another template; keep walking up - // from its __dataHost - if (model.__dataHost != template) { - node = model.__dataHost; - } else { - return model; - } - } else { - // Still in a template scope, keep going up until - // a __templatizeInstance is found - node = wrap(node).parentNode; - } - } - return null; -} - -export { TemplateInstanceBase };
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/unresolved.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/unresolved.js deleted file mode 100644 index a7c8fc84..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/unresolved.js +++ /dev/null
@@ -1,21 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -function resolve() { - document.body.removeAttribute('unresolved'); -} - -if (document.readyState === 'interactive' || document.readyState === 'complete') { - resolve(); -} else { - window.addEventListener('DOMContentLoaded', resolve); -} - -export {};
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/wrap.js b/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/wrap.js deleted file mode 100644 index ece21a4e..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/lib/utils/wrap.js +++ /dev/null
@@ -1,23 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -/* eslint-disable valid-jsdoc */ -/** - * Node wrapper to ensure ShadowDOM safe operation regardless of polyfill - * presence or mode. Note that with the introduction of `ShadyDOM.noPatch`, - * a node wrapper must be used to access ShadowDOM API. - * This is similar to using `Polymer.dom` but relies exclusively - * on the presence of the ShadyDOM polyfill rather than requiring the loading - * of legacy (Polymer.dom) API. - * @type {function(Node):Node} - */ -export const wrap = (window['ShadyDOM'] && window['ShadyDOM']['noPatch'] && window['ShadyDOM']['wrap']) ? - window['ShadyDOM']['wrap'] : (n) => n; -
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/polymer-element.js b/third_party/polymer/v3_0/components-chromium/polymer/polymer-element.js deleted file mode 100644 index 1179d68..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/polymer-element.js +++ /dev/null
@@ -1,31 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -import { ElementMixin, version } from './lib/mixins/element-mixin.js'; -export { html } from './lib/utils/html-tag.js'; - -export { version }; - -/** - * Base class that provides the core API for Polymer's meta-programming - * features including template stamping, data-binding, attribute deserialization, - * and property change observation. - * - * @customElement - * @polymer - * @constructor - * @implements {Polymer_ElementMixin} - * @extends HTMLElement - * @appliesMixin ElementMixin - * @summary Custom element base class that provides the core API for Polymer's - * key meta-programming features including template stamping, data-binding, - * attribute deserialization, and property change observation - */ -export const PolymerElement = ElementMixin(HTMLElement);
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/polymer-legacy.js b/third_party/polymer/v3_0/components-chromium/polymer/polymer-legacy.js deleted file mode 100644 index ffdd35e0..0000000 --- a/third_party/polymer/v3_0/components-chromium/polymer/polymer-legacy.js +++ /dev/null
@@ -1,27 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -import { LegacyElementMixin } from './lib/legacy/legacy-element-mixin.js'; -export { Polymer } from './lib/legacy/polymer-fn.js'; -/* template elements */ -import './lib/legacy/templatizer-behavior.js'; -import './lib/elements/dom-bind.js'; -import './lib/elements/dom-repeat.js'; -import './lib/elements/dom-if.js'; -import './lib/elements/array-selector.js'; -/* custom-style */ -import './lib/elements/custom-style.js'; -/* bc behaviors */ -import './lib/legacy/mutable-data-behavior.js'; -/* import html-tag to export html */ -export { html } from './lib/utils/html-tag.js'; - -// bc -export const Base = LegacyElementMixin(HTMLElement).prototype;
diff --git a/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js b/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js new file mode 100644 index 0000000..5c2c4c59 --- /dev/null +++ b/third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js
@@ -0,0 +1 @@ +window.JSCompiler_renameProperty=function(prop,obj){return prop};let microtaskCurrHandle=0;let microtaskLastHandle=0;let microtaskCallbacks=[];let microtaskNodeContent=0;let microtaskNode=document.createTextNode("");new window.MutationObserver(microtaskFlush).observe(microtaskNode,{characterData:true});function microtaskFlush(){const len=microtaskCallbacks.length;for(let i=0;i<len;i++){let cb=microtaskCallbacks[i];if(cb){try{cb()}catch(e){setTimeout(()=>{throw e})}}}microtaskCallbacks.splice(0,len);microtaskLastHandle+=len}const timeOut={after(delay){return{run(fn){return window.setTimeout(fn,delay)},cancel(handle){window.clearTimeout(handle)}}},run(fn,delay){return window.setTimeout(fn,delay)},cancel(handle){window.clearTimeout(handle)}};const animationFrame={run(fn){return window.requestAnimationFrame(fn)},cancel(handle){window.cancelAnimationFrame(handle)}};const idlePeriod={run(fn){return window.requestIdleCallback?window.requestIdleCallback(fn):window.setTimeout(fn,16)},cancel(handle){window.cancelIdleCallback?window.cancelIdleCallback(handle):window.clearTimeout(handle)}};const microTask={run(callback){microtaskNode.textContent=microtaskNodeContent++;microtaskCallbacks.push(callback);return microtaskCurrHandle++},cancel(handle){const idx=handle-microtaskLastHandle;if(idx>=0){if(!microtaskCallbacks[idx]){throw new Error("invalid async handle: "+handle)}microtaskCallbacks[idx]=null}}};let dedupeId=0;const dedupingMixin=function(mixin){let mixinApplications=mixin.__mixinApplications;if(!mixinApplications){mixinApplications=new WeakMap;mixin.__mixinApplications=mixinApplications}let mixinDedupeId=dedupeId++;function dedupingMixin(base){let baseSet=base.__mixinSet;if(baseSet&&baseSet[mixinDedupeId]){return base}let map=mixinApplications;let extended=map.get(base);if(!extended){extended=mixin(base);map.set(base,extended)}let mixinSet=Object.create(extended.__mixinSet||baseSet||null);mixinSet[mixinDedupeId]=true;extended.__mixinSet=mixinSet;return extended}return dedupingMixin};class Debouncer{constructor(){this._asyncModule=null;this._callback=null;this._timer=null}setConfig(asyncModule,callback){this._asyncModule=asyncModule;this._callback=callback;this._timer=this._asyncModule.run(()=>{this._timer=null;debouncerQueue.delete(this);this._callback()})}cancel(){if(this.isActive()){this._cancelAsync();debouncerQueue.delete(this)}}_cancelAsync(){if(this.isActive()){this._asyncModule.cancel(this._timer);this._timer=null}}flush(){if(this.isActive()){this.cancel();this._callback()}}isActive(){return this._timer!=null}static debounce(debouncer,asyncModule,callback){if(debouncer instanceof Debouncer){debouncer._cancelAsync()}else{debouncer=new Debouncer}debouncer.setConfig(asyncModule,callback);return debouncer}}let debouncerQueue=new Set;const enqueueDebouncer=function(debouncer){debouncerQueue.add(debouncer)};const flushDebouncers=function(){const didFlush=Boolean(debouncerQueue.size);debouncerQueue.forEach(debouncer=>{try{debouncer.flush()}catch(e){setTimeout(()=>{throw e})}});return didFlush};let CSS_URL_RX=/(url\()([^)]*)(\))/g;let ABS_URL=/(^\/)|(^#)|(^[\w-\d]*:)/;let workingURL;let resolveDoc;function resolveUrl(url,baseURI){if(url&&ABS_URL.test(url)){return url}if(workingURL===undefined){workingURL=false;try{const u=new URL("b","http://a");u.pathname="c%20d";workingURL=u.href==="http://a/c%20d"}catch(e){}}if(!baseURI){baseURI=document.baseURI||window.location.href}if(workingURL){return new URL(url,baseURI).href}if(!resolveDoc){resolveDoc=document.implementation.createHTMLDocument("temp");resolveDoc.base=resolveDoc.createElement("base");resolveDoc.head.appendChild(resolveDoc.base);resolveDoc.anchor=resolveDoc.createElement("a");resolveDoc.body.appendChild(resolveDoc.anchor)}resolveDoc.base.href=baseURI;resolveDoc.anchor.href=url;return resolveDoc.anchor.href||url}function resolveCss(cssText,baseURI){return cssText.replace(CSS_URL_RX,function(m,pre,url,post){return pre+"'"+resolveUrl(url.replace(/["']/g,""),baseURI)+"'"+post})}function pathFromUrl(url){return url.substring(0,url.lastIndexOf("/")+1)}const useShadow=!window.ShadyDOM;const useNativeCSSProperties=Boolean(!window.ShadyCSS||window.ShadyCSS.nativeCss);const useNativeCustomElements=!window.customElements.polyfillWrapFlushCallback;let rootPath=pathFromUrl(document.baseURI||window.location.href);let sanitizeDOMValue=window.Polymer&&window.Polymer.sanitizeDOMValue||undefined;let passiveTouchGestures=false;let strictTemplatePolicy=false;let allowTemplateFromDomModule=false;let legacyOptimizations=false;let syncInitialRender=false;const wrap=window["ShadyDOM"]&&window["ShadyDOM"]["noPatch"]&&window["ShadyDOM"]["wrap"]?window["ShadyDOM"]["wrap"]:n=>n;let HAS_NATIVE_TA=typeof document.head.style.touchAction==="string";let GESTURE_KEY="__polymerGestures";let HANDLED_OBJ="__polymerGesturesHandled";let TOUCH_ACTION="__polymerGesturesTouchAction";let TAP_DISTANCE=25;let TRACK_DISTANCE=5;let TRACK_LENGTH=2;let MOUSE_TIMEOUT=2500;let MOUSE_EVENTS=["mousedown","mousemove","mouseup","click"];let MOUSE_WHICH_TO_BUTTONS=[0,1,4,2];let MOUSE_HAS_BUTTONS=function(){try{return new MouseEvent("test",{buttons:1}).buttons===1}catch(e){return false}}();function isMouseEvent(name){return MOUSE_EVENTS.indexOf(name)>-1}let SUPPORTS_PASSIVE=false;(function(){try{let opts=Object.defineProperty({},"passive",{get(){SUPPORTS_PASSIVE=true}});window.addEventListener("test",null,opts);window.removeEventListener("test",null,opts)}catch(e){}})();function PASSIVE_TOUCH(eventName){if(isMouseEvent(eventName)||eventName==="touchend"){return}if(HAS_NATIVE_TA&&SUPPORTS_PASSIVE&&passiveTouchGestures){return{passive:true}}else{return}}let IS_TOUCH_ONLY=navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);const clickedLabels=[];const labellable={button:true,input:true,keygen:true,meter:true,output:true,textarea:true,progress:true,select:true};const canBeDisabled={button:true,command:true,fieldset:true,input:true,keygen:true,optgroup:true,option:true,select:true,textarea:true};function canBeLabelled(el){return labellable[el.localName]||false}function matchingLabels(el){let labels=Array.prototype.slice.call(el.labels||[]);if(!labels.length){labels=[];let root=el.getRootNode();if(el.id){let matching=root.querySelectorAll(`label[for = ${el.id}]`);for(let i=0;i<matching.length;i++){labels.push(matching[i])}}}return labels}let mouseCanceller=function(mouseEvent){let sc=mouseEvent.sourceCapabilities;if(sc&&!sc.firesTouchEvents){return}mouseEvent[HANDLED_OBJ]={skip:true};if(mouseEvent.type==="click"){let clickFromLabel=false;let path=getComposedPath(mouseEvent);for(let i=0;i<path.length;i++){if(path[i].nodeType===Node.ELEMENT_NODE){if(path[i].localName==="label"){clickedLabels.push(path[i])}else if(canBeLabelled(path[i])){let ownerLabels=matchingLabels(path[i]);for(let j=0;j<ownerLabels.length;j++){clickFromLabel=clickFromLabel||clickedLabels.indexOf(ownerLabels[j])>-1}}}if(path[i]===POINTERSTATE.mouse.target){return}}if(clickFromLabel){return}mouseEvent.preventDefault();mouseEvent.stopPropagation()}};function setupTeardownMouseCanceller(setup){let events=IS_TOUCH_ONLY?["click"]:MOUSE_EVENTS;for(let i=0,en;i<events.length;i++){en=events[i];if(setup){clickedLabels.length=0;document.addEventListener(en,mouseCanceller,true)}else{document.removeEventListener(en,mouseCanceller,true)}}}function ignoreMouse(e){if(!POINTERSTATE.mouse.mouseIgnoreJob){setupTeardownMouseCanceller(true)}let unset=function(){setupTeardownMouseCanceller();POINTERSTATE.mouse.target=null;POINTERSTATE.mouse.mouseIgnoreJob=null};POINTERSTATE.mouse.target=getComposedPath(e)[0];POINTERSTATE.mouse.mouseIgnoreJob=Debouncer.debounce(POINTERSTATE.mouse.mouseIgnoreJob,timeOut.after(MOUSE_TIMEOUT),unset)}function hasLeftMouseButton(ev){let type=ev.type;if(!isMouseEvent(type)){return false}if(type==="mousemove"){let buttons=ev.buttons===undefined?1:ev.buttons;if(ev instanceof window.MouseEvent&&!MOUSE_HAS_BUTTONS){buttons=MOUSE_WHICH_TO_BUTTONS[ev.which]||0}return Boolean(buttons&1)}else{let button=ev.button===undefined?0:ev.button;return button===0}}function isSyntheticClick(ev){if(ev.type==="click"){if(ev.detail===0){return true}let t=_findOriginalTarget(ev);if(!t.nodeType||t.nodeType!==Node.ELEMENT_NODE){return true}let bcr=t.getBoundingClientRect();let x=ev.pageX,y=ev.pageY;return!(x>=bcr.left&&x<=bcr.right&&(y>=bcr.top&&y<=bcr.bottom))}return false}let POINTERSTATE={mouse:{target:null,mouseIgnoreJob:null},touch:{x:0,y:0,id:-1,scrollDecided:false}};function firstTouchAction(ev){let ta="auto";let path=getComposedPath(ev);for(let i=0,n;i<path.length;i++){n=path[i];if(n[TOUCH_ACTION]){ta=n[TOUCH_ACTION];break}}return ta}function trackDocument(stateObj,movefn,upfn){stateObj.movefn=movefn;stateObj.upfn=upfn;document.addEventListener("mousemove",movefn);document.addEventListener("mouseup",upfn)}function untrackDocument(stateObj){document.removeEventListener("mousemove",stateObj.movefn);document.removeEventListener("mouseup",stateObj.upfn);stateObj.movefn=null;stateObj.upfn=null}document.addEventListener("touchend",ignoreMouse,SUPPORTS_PASSIVE?{passive:true}:false);const getComposedPath=window.ShadyDOM&&window.ShadyDOM.noPatch?window.ShadyDOM.composedPath:event=>event.composedPath&&event.composedPath()||[];const gestures={};const recognizers=[];function deepTargetFind(x,y){let node=document.elementFromPoint(x,y);let next=node;while(next&&next.shadowRoot&&!window.ShadyDOM){let oldNext=next;next=next.shadowRoot.elementFromPoint(x,y);if(oldNext===next){break}if(next){node=next}}return node}function _findOriginalTarget(ev){const path=getComposedPath(ev);return path.length>0?path[0]:ev.target}function _handleNative(ev){let handled;let type=ev.type;let node=ev.currentTarget;let gobj=node[GESTURE_KEY];if(!gobj){return}let gs=gobj[type];if(!gs){return}if(!ev[HANDLED_OBJ]){ev[HANDLED_OBJ]={};if(type.slice(0,5)==="touch"){ev=ev;let t=ev.changedTouches[0];if(type==="touchstart"){if(ev.touches.length===1){POINTERSTATE.touch.id=t.identifier}}if(POINTERSTATE.touch.id!==t.identifier){return}if(!HAS_NATIVE_TA){if(type==="touchstart"||type==="touchmove"){_handleTouchAction(ev)}}}}handled=ev[HANDLED_OBJ];if(handled.skip){return}for(let i=0,r;i<recognizers.length;i++){r=recognizers[i];if(gs[r.name]&&!handled[r.name]){if(r.flow&&r.flow.start.indexOf(ev.type)>-1&&r.reset){r.reset()}}}for(let i=0,r;i<recognizers.length;i++){r=recognizers[i];if(gs[r.name]&&!handled[r.name]){handled[r.name]=true;r[type](ev)}}}function _handleTouchAction(ev){let t=ev.changedTouches[0];let type=ev.type;if(type==="touchstart"){POINTERSTATE.touch.x=t.clientX;POINTERSTATE.touch.y=t.clientY;POINTERSTATE.touch.scrollDecided=false}else if(type==="touchmove"){if(POINTERSTATE.touch.scrollDecided){return}POINTERSTATE.touch.scrollDecided=true;let ta=firstTouchAction(ev);let shouldPrevent=false;let dx=Math.abs(POINTERSTATE.touch.x-t.clientX);let dy=Math.abs(POINTERSTATE.touch.y-t.clientY);if(!ev.cancelable);else if(ta==="none"){shouldPrevent=true}else if(ta==="pan-x"){shouldPrevent=dy>dx}else if(ta==="pan-y"){shouldPrevent=dx>dy}if(shouldPrevent){ev.preventDefault()}else{prevent("track")}}}function addListener(node,evType,handler){if(gestures[evType]){_add(node,evType,handler);return true}return false}function removeListener(node,evType,handler){if(gestures[evType]){_remove(node,evType,handler);return true}return false}function _add(node,evType,handler){let recognizer=gestures[evType];let deps=recognizer.deps;let name=recognizer.name;let gobj=node[GESTURE_KEY];if(!gobj){node[GESTURE_KEY]=gobj={}}for(let i=0,dep,gd;i<deps.length;i++){dep=deps[i];if(IS_TOUCH_ONLY&&isMouseEvent(dep)&&dep!=="click"){continue}gd=gobj[dep];if(!gd){gobj[dep]=gd={_count:0}}if(gd._count===0){node.addEventListener(dep,_handleNative,PASSIVE_TOUCH(dep))}gd[name]=(gd[name]||0)+1;gd._count=(gd._count||0)+1}node.addEventListener(evType,handler);if(recognizer.touchAction){setTouchAction(node,recognizer.touchAction)}}function _remove(node,evType,handler){let recognizer=gestures[evType];let deps=recognizer.deps;let name=recognizer.name;let gobj=node[GESTURE_KEY];if(gobj){for(let i=0,dep,gd;i<deps.length;i++){dep=deps[i];gd=gobj[dep];if(gd&&gd[name]){gd[name]=(gd[name]||1)-1;gd._count=(gd._count||1)-1;if(gd._count===0){node.removeEventListener(dep,_handleNative,PASSIVE_TOUCH(dep))}}}}node.removeEventListener(evType,handler)}function register(recog){recognizers.push(recog);for(let i=0;i<recog.emits.length;i++){gestures[recog.emits[i]]=recog}}function _findRecognizerByEvent(evName){for(let i=0,r;i<recognizers.length;i++){r=recognizers[i];for(let j=0,n;j<r.emits.length;j++){n=r.emits[j];if(n===evName){return r}}}return null}function setTouchAction(node,value){if(HAS_NATIVE_TA&&node instanceof HTMLElement){microTask.run(()=>{node.style.touchAction=value})}node[TOUCH_ACTION]=value}function _fire(target,type,detail){let ev=new Event(type,{bubbles:true,cancelable:true,composed:true});ev.detail=detail;wrap(target).dispatchEvent(ev);if(ev.defaultPrevented){let preventer=detail.preventer||detail.sourceEvent;if(preventer&&preventer.preventDefault){preventer.preventDefault()}}}function prevent(evName){let recognizer=_findRecognizerByEvent(evName);if(recognizer.info){recognizer.info.prevent=true}}function resetMouseCanceller(){if(POINTERSTATE.mouse.mouseIgnoreJob){POINTERSTATE.mouse.mouseIgnoreJob.flush()}}register({name:"downup",deps:["mousedown","touchstart","touchend"],flow:{start:["mousedown","touchstart"],end:["mouseup","touchend"]},emits:["down","up"],info:{movefn:null,upfn:null},reset:function(){untrackDocument(this.info)},mousedown:function(e){if(!hasLeftMouseButton(e)){return}let t=_findOriginalTarget(e);let self=this;let movefn=function movefn(e){if(!hasLeftMouseButton(e)){downupFire("up",t,e);untrackDocument(self.info)}};let upfn=function upfn(e){if(hasLeftMouseButton(e)){downupFire("up",t,e)}untrackDocument(self.info)};trackDocument(this.info,movefn,upfn);downupFire("down",t,e)},touchstart:function(e){downupFire("down",_findOriginalTarget(e),e.changedTouches[0],e)},touchend:function(e){downupFire("up",_findOriginalTarget(e),e.changedTouches[0],e)}});function downupFire(type,target,event,preventer){if(!target){return}_fire(target,type,{x:event.clientX,y:event.clientY,sourceEvent:event,preventer:preventer,prevent:function(e){return prevent(e)}})}register({name:"track",touchAction:"none",deps:["mousedown","touchstart","touchmove","touchend"],flow:{start:["mousedown","touchstart"],end:["mouseup","touchend"]},emits:["track"],info:{x:0,y:0,state:"start",started:false,moves:[],addMove:function(move){if(this.moves.length>TRACK_LENGTH){this.moves.shift()}this.moves.push(move)},movefn:null,upfn:null,prevent:false},reset:function(){this.info.state="start";this.info.started=false;this.info.moves=[];this.info.x=0;this.info.y=0;this.info.prevent=false;untrackDocument(this.info)},mousedown:function(e){if(!hasLeftMouseButton(e)){return}let t=_findOriginalTarget(e);let self=this;let movefn=function movefn(e){let x=e.clientX,y=e.clientY;if(trackHasMovedEnough(self.info,x,y)){self.info.state=self.info.started?e.type==="mouseup"?"end":"track":"start";if(self.info.state==="start"){prevent("tap")}self.info.addMove({x:x,y:y});if(!hasLeftMouseButton(e)){self.info.state="end";untrackDocument(self.info)}if(t){trackFire(self.info,t,e)}self.info.started=true}};let upfn=function upfn(e){if(self.info.started){movefn(e)}untrackDocument(self.info)};trackDocument(this.info,movefn,upfn);this.info.x=e.clientX;this.info.y=e.clientY},touchstart:function(e){let ct=e.changedTouches[0];this.info.x=ct.clientX;this.info.y=ct.clientY},touchmove:function(e){let t=_findOriginalTarget(e);let ct=e.changedTouches[0];let x=ct.clientX,y=ct.clientY;if(trackHasMovedEnough(this.info,x,y)){if(this.info.state==="start"){prevent("tap")}this.info.addMove({x:x,y:y});trackFire(this.info,t,ct);this.info.state="track";this.info.started=true}},touchend:function(e){let t=_findOriginalTarget(e);let ct=e.changedTouches[0];if(this.info.started){this.info.state="end";this.info.addMove({x:ct.clientX,y:ct.clientY});trackFire(this.info,t,ct)}}});function trackHasMovedEnough(info,x,y){if(info.prevent){return false}if(info.started){return true}let dx=Math.abs(info.x-x);let dy=Math.abs(info.y-y);return dx>=TRACK_DISTANCE||dy>=TRACK_DISTANCE}function trackFire(info,target,touch){if(!target){return}let secondlast=info.moves[info.moves.length-2];let lastmove=info.moves[info.moves.length-1];let dx=lastmove.x-info.x;let dy=lastmove.y-info.y;let ddx,ddy=0;if(secondlast){ddx=lastmove.x-secondlast.x;ddy=lastmove.y-secondlast.y}_fire(target,"track",{state:info.state,x:touch.clientX,y:touch.clientY,dx:dx,dy:dy,ddx:ddx,ddy:ddy,sourceEvent:touch,hover:function(){return deepTargetFind(touch.clientX,touch.clientY)}})}register({name:"tap",deps:["mousedown","click","touchstart","touchend"],flow:{start:["mousedown","touchstart"],end:["click","touchend"]},emits:["tap"],info:{x:NaN,y:NaN,prevent:false},reset:function(){this.info.x=NaN;this.info.y=NaN;this.info.prevent=false},mousedown:function(e){if(hasLeftMouseButton(e)){this.info.x=e.clientX;this.info.y=e.clientY}},click:function(e){if(hasLeftMouseButton(e)){trackForward(this.info,e)}},touchstart:function(e){const touch=e.changedTouches[0];this.info.x=touch.clientX;this.info.y=touch.clientY},touchend:function(e){trackForward(this.info,e.changedTouches[0],e)}});function trackForward(info,e,preventer){let dx=Math.abs(e.clientX-info.x);let dy=Math.abs(e.clientY-info.y);let t=_findOriginalTarget(preventer||e);if(!t||canBeDisabled[t.localName]&&t.hasAttribute("disabled")){return}if(isNaN(dx)||isNaN(dy)||dx<=TAP_DISTANCE&&dy<=TAP_DISTANCE||isSyntheticClick(e)){if(!info.prevent){_fire(t,"tap",{x:e.clientX,y:e.clientY,sourceEvent:e,preventer:preventer})}}}const findOriginalTarget=_findOriginalTarget;const add=addListener;const remove=removeListener;var gestures$1=Object.freeze({gestures:gestures,recognizers:recognizers,deepTargetFind:deepTargetFind,addListener:addListener,removeListener:removeListener,register:register,setTouchAction:setTouchAction,prevent:prevent,resetMouseCanceller:resetMouseCanceller,findOriginalTarget:findOriginalTarget,add:add,remove:remove});const nativeShadow=!(window["ShadyDOM"]&&window["ShadyDOM"]["inUse"]);let nativeCssVariables_;function calcCssVariables(settings){if(settings&&settings["shimcssproperties"]){nativeCssVariables_=false}else{nativeCssVariables_=nativeShadow||Boolean(!navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)&&window.CSS&&CSS.supports&&CSS.supports("box-shadow","0 0 0 var(--foo)"))}}let cssBuild;if(window.ShadyCSS&&window.ShadyCSS.cssBuild!==undefined){cssBuild=window.ShadyCSS.cssBuild}const disableRuntime=Boolean(window.ShadyCSS&&window.ShadyCSS.disableRuntime);if(window.ShadyCSS&&window.ShadyCSS.nativeCss!==undefined){nativeCssVariables_=window.ShadyCSS.nativeCss}else if(window.ShadyCSS){calcCssVariables(window.ShadyCSS);window.ShadyCSS=undefined}else{calcCssVariables(window["WebComponents"]&&window["WebComponents"]["flags"])}const nativeCssVariables=nativeCssVariables_;class StyleNode{constructor(){this["start"]=0;this["end"]=0;this["previous"]=null;this["parent"]=null;this["rules"]=null;this["parsedCssText"]="";this["cssText"]="";this["atRule"]=false;this["type"]=0;this["keyframesName"]="";this["selector"]="";this["parsedSelector"]=""}}function parse(text){text=clean(text);return parseCss(lex(text),text)}function clean(cssText){return cssText.replace(RX.comments,"").replace(RX.port,"")}function lex(text){let root=new StyleNode;root["start"]=0;root["end"]=text.length;let n=root;for(let i=0,l=text.length;i<l;i++){if(text[i]===OPEN_BRACE){if(!n["rules"]){n["rules"]=[]}let p=n;let previous=p["rules"][p["rules"].length-1]||null;n=new StyleNode;n["start"]=i+1;n["parent"]=p;n["previous"]=previous;p["rules"].push(n)}else if(text[i]===CLOSE_BRACE){n["end"]=i+1;n=n["parent"]||root}}return root}function parseCss(node,text){let t=text.substring(node["start"],node["end"]-1);node["parsedCssText"]=node["cssText"]=t.trim();if(node["parent"]){let ss=node["previous"]?node["previous"]["end"]:node["parent"]["start"];t=text.substring(ss,node["start"]-1);t=_expandUnicodeEscapes(t);t=t.replace(RX.multipleSpaces," ");t=t.substring(t.lastIndexOf(";")+1);let s=node["parsedSelector"]=node["selector"]=t.trim();node["atRule"]=s.indexOf(AT_START)===0;if(node["atRule"]){if(s.indexOf(MEDIA_START)===0){node["type"]=types.MEDIA_RULE}else if(s.match(RX.keyframesRule)){node["type"]=types.KEYFRAMES_RULE;node["keyframesName"]=node["selector"].split(RX.multipleSpaces).pop()}}else{if(s.indexOf(VAR_START)===0){node["type"]=types.MIXIN_RULE}else{node["type"]=types.STYLE_RULE}}}let r$=node["rules"];if(r$){for(let i=0,l=r$.length,r;i<l&&(r=r$[i]);i++){parseCss(r,text)}}return node}function _expandUnicodeEscapes(s){return s.replace(/\\([0-9a-f]{1,6})\s/gi,function(){let code=arguments[1],repeat=6-code.length;while(repeat--){code="0"+code}return"\\"+code})}function stringify(node,preserveProperties,text=""){let cssText="";if(node["cssText"]||node["rules"]){let r$=node["rules"];if(r$&&!_hasMixinRules(r$)){for(let i=0,l=r$.length,r;i<l&&(r=r$[i]);i++){cssText=stringify(r,preserveProperties,cssText)}}else{cssText=preserveProperties?node["cssText"]:removeCustomProps(node["cssText"]);cssText=cssText.trim();if(cssText){cssText=" "+cssText+"\n"}}}if(cssText){if(node["selector"]){text+=node["selector"]+" "+OPEN_BRACE+"\n"}text+=cssText;if(node["selector"]){text+=CLOSE_BRACE+"\n\n"}}return text}function _hasMixinRules(rules){let r=rules[0];return Boolean(r)&&Boolean(r["selector"])&&r["selector"].indexOf(VAR_START)===0}function removeCustomProps(cssText){cssText=removeCustomPropAssignment(cssText);return removeCustomPropApply(cssText)}function removeCustomPropAssignment(cssText){return cssText.replace(RX.customProp,"").replace(RX.mixinProp,"")}function removeCustomPropApply(cssText){return cssText.replace(RX.mixinApply,"").replace(RX.varApply,"")}const types={STYLE_RULE:1,KEYFRAMES_RULE:7,MEDIA_RULE:4,MIXIN_RULE:1e3};const OPEN_BRACE="{";const CLOSE_BRACE="}";const RX={comments:/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,port:/@import[^;]*;/gim,customProp:/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,mixinProp:/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,mixinApply:/@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,varApply:/[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,keyframesRule:/^@[^\s]*keyframes/,multipleSpaces:/\s+/g};const VAR_START="--";const MEDIA_START="@media";const AT_START="@";const VAR_ASSIGN=/(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi;const MIXIN_MATCH=/(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi;const MEDIA_MATCH=/@media\s(.*)/;const styleTextSet=new Set;const scopingAttribute="shady-unscoped";function processUnscopedStyle(style){const text=style.textContent;if(!styleTextSet.has(text)){styleTextSet.add(text);const newStyle=style.cloneNode(true);document.head.appendChild(newStyle)}}function isUnscopedStyle(style){return style.hasAttribute(scopingAttribute)}function toCssText(rules,callback){if(!rules){return""}if(typeof rules==="string"){rules=parse(rules)}if(callback){forEachRule(rules,callback)}return stringify(rules,nativeCssVariables)}function rulesForStyle(style){if(!style["__cssRules"]&&style.textContent){style["__cssRules"]=parse(style.textContent)}return style["__cssRules"]||null}function forEachRule(node,styleRuleCallback,keyframesRuleCallback,onlyActiveRules){if(!node){return}let skipRules=false;let type=node["type"];if(onlyActiveRules){if(type===types.MEDIA_RULE){let matchMedia=node["selector"].match(MEDIA_MATCH);if(matchMedia){if(!window.matchMedia(matchMedia[1]).matches){skipRules=true}}}}if(type===types.STYLE_RULE){styleRuleCallback(node)}else if(keyframesRuleCallback&&type===types.KEYFRAMES_RULE){keyframesRuleCallback(node)}else if(type===types.MIXIN_RULE){skipRules=true}let r$=node["rules"];if(r$&&!skipRules){for(let i=0,l=r$.length,r;i<l&&(r=r$[i]);i++){forEachRule(r,styleRuleCallback,keyframesRuleCallback,onlyActiveRules)}}}function findMatchingParen(text,start){let level=0;for(let i=start,l=text.length;i<l;i++){if(text[i]==="("){level++}else if(text[i]===")"){if(--level===0){return i}}}return-1}function processVariableAndFallback(str,callback){let start=str.indexOf("var(");if(start===-1){return callback(str,"","","")}let end=findMatchingParen(str,start+3);let inner=str.substring(start+4,end);let prefix=str.substring(0,start);let suffix=processVariableAndFallback(str.substring(end+1),callback);let comma=inner.indexOf(",");if(comma===-1){return callback(prefix,inner.trim(),"",suffix)}let value=inner.substring(0,comma).trim();let fallback=inner.substring(comma+1).trim();return callback(prefix,value,fallback,suffix)}const wrap$1=window["ShadyDOM"]&&window["ShadyDOM"]["wrap"]||(node=>node);function getIsExtends(element){let localName=element["localName"];let is="",typeExtension="";if(localName){if(localName.indexOf("-")>-1){is=localName}else{typeExtension=localName;is=element.getAttribute&&element.getAttribute("is")||""}}else{is=element.is;typeExtension=element.extends}return{is:is,typeExtension:typeExtension}}function gatherStyleText(element){const styleTextParts=[];const styles=element.querySelectorAll("style");for(let i=0;i<styles.length;i++){const style=styles[i];if(isUnscopedStyle(style)){if(!nativeShadow){processUnscopedStyle(style);style.parentNode.removeChild(style)}}else{styleTextParts.push(style.textContent);style.parentNode.removeChild(style)}}return styleTextParts.join("").trim()}const CSS_BUILD_ATTR="css-build";function getCssBuild(element){if(cssBuild!==undefined){return cssBuild}if(element.__cssBuild===undefined){const attrValue=element.getAttribute(CSS_BUILD_ATTR);if(attrValue){element.__cssBuild=attrValue}else{const buildComment=getBuildComment(element);if(buildComment!==""){removeBuildComment(element)}element.__cssBuild=buildComment}}return element.__cssBuild||""}function elementHasBuiltCss(element){return getCssBuild(element)!==""}function getBuildComment(element){const buildComment=element.localName==="template"?element.content.firstChild:element.firstChild;if(buildComment instanceof Comment){const commentParts=buildComment.textContent.trim().split(":");if(commentParts[0]===CSS_BUILD_ATTR){return commentParts[1]}}return""}function removeBuildComment(element){const buildComment=element.localName==="template"?element.content.firstChild:element.firstChild;buildComment.parentNode.removeChild(buildComment)}function updateNativeProperties(element,properties){for(let p in properties){if(p===null){element.style.removeProperty(p)}else{element.style.setProperty(p,properties[p])}}}function getComputedStyleValue(element,property){const value=window.getComputedStyle(element).getPropertyValue(property);if(!value){return""}else{return value.trim()}}function detectMixin(cssText){const has=MIXIN_MATCH.test(cssText)||VAR_ASSIGN.test(cssText);MIXIN_MATCH.lastIndex=0;VAR_ASSIGN.lastIndex=0;return has}const APPLY_NAME_CLEAN=/;\s*/m;const INITIAL_INHERIT=/^\s*(initial)|(inherit)\s*$/;const IMPORTANT=/\s*!important/;const MIXIN_VAR_SEP="_-_";class MixinMap{constructor(){this._map={}}set(name,props){name=name.trim();this._map[name]={properties:props,dependants:{}}}get(name){name=name.trim();return this._map[name]||null}}let invalidCallback=null;class ApplyShim{constructor(){this._currentElement=null;this._measureElement=null;this._map=new MixinMap}detectMixin(cssText){return detectMixin(cssText)}gatherStyles(template){const styleText=gatherStyleText(template.content);if(styleText){const style=document.createElement("style");style.textContent=styleText;template.content.insertBefore(style,template.content.firstChild);return style}return null}transformTemplate(template,elementName){if(template._gatheredStyle===undefined){template._gatheredStyle=this.gatherStyles(template)}const style=template._gatheredStyle;return style?this.transformStyle(style,elementName):null}transformStyle(style,elementName=""){let ast=rulesForStyle(style);this.transformRules(ast,elementName);style.textContent=toCssText(ast);return ast}transformCustomStyle(style){let ast=rulesForStyle(style);forEachRule(ast,rule=>{if(rule["selector"]===":root"){rule["selector"]="html"}this.transformRule(rule)});style.textContent=toCssText(ast);return ast}transformRules(rules,elementName){this._currentElement=elementName;forEachRule(rules,r=>{this.transformRule(r)});this._currentElement=null}transformRule(rule){rule["cssText"]=this.transformCssText(rule["parsedCssText"],rule);if(rule["selector"]===":root"){rule["selector"]=":host > *"}}transformCssText(cssText,rule){cssText=cssText.replace(VAR_ASSIGN,(matchText,propertyName,valueProperty,valueMixin)=>this._produceCssProperties(matchText,propertyName,valueProperty,valueMixin,rule));return this._consumeCssProperties(cssText,rule)}_getInitialValueForProperty(property){if(!this._measureElement){this._measureElement=document.createElement("meta");this._measureElement.setAttribute("apply-shim-measure","");this._measureElement.style.all="initial";document.head.appendChild(this._measureElement)}return window.getComputedStyle(this._measureElement).getPropertyValue(property)}_fallbacksFromPreviousRules(startRule){let topRule=startRule;while(topRule["parent"]){topRule=topRule["parent"]}const fallbacks={};let seenStartRule=false;forEachRule(topRule,r=>{seenStartRule=seenStartRule||r===startRule;if(seenStartRule){return}if(r["selector"]===startRule["selector"]){Object.assign(fallbacks,this._cssTextToMap(r["parsedCssText"]))}});return fallbacks}_consumeCssProperties(text,rule){let m=null;while(m=MIXIN_MATCH.exec(text)){let matchText=m[0];let mixinName=m[1];let idx=m.index;let applyPos=idx+matchText.indexOf("@apply");let afterApplyPos=idx+matchText.length;let textBeforeApply=text.slice(0,applyPos);let textAfterApply=text.slice(afterApplyPos);let defaults=rule?this._fallbacksFromPreviousRules(rule):{};Object.assign(defaults,this._cssTextToMap(textBeforeApply));let replacement=this._atApplyToCssProperties(mixinName,defaults);text=`${textBeforeApply}${replacement}${textAfterApply}`;MIXIN_MATCH.lastIndex=idx+replacement.length}return text}_atApplyToCssProperties(mixinName,fallbacks){mixinName=mixinName.replace(APPLY_NAME_CLEAN,"");let vars=[];let mixinEntry=this._map.get(mixinName);if(!mixinEntry){this._map.set(mixinName,{});mixinEntry=this._map.get(mixinName)}if(mixinEntry){if(this._currentElement){mixinEntry.dependants[this._currentElement]=true}let p,parts,f;const properties=mixinEntry.properties;for(p in properties){f=fallbacks&&fallbacks[p];parts=[p,": var(",mixinName,MIXIN_VAR_SEP,p];if(f){parts.push(",",f.replace(IMPORTANT,""))}parts.push(")");if(IMPORTANT.test(properties[p])){parts.push(" !important")}vars.push(parts.join(""))}}return vars.join("; ")}_replaceInitialOrInherit(property,value){let match=INITIAL_INHERIT.exec(value);if(match){if(match[1]){value=this._getInitialValueForProperty(property)}else{value="apply-shim-inherit"}}return value}_cssTextToMap(text,replaceInitialOrInherit=false){let props=text.split(";");let property,value;let out={};for(let i=0,p,sp;i<props.length;i++){p=props[i];if(p){sp=p.split(":");if(sp.length>1){property=sp[0].trim();value=sp.slice(1).join(":");if(replaceInitialOrInherit){value=this._replaceInitialOrInherit(property,value)}out[property]=value}}}return out}_invalidateMixinEntry(mixinEntry){if(!invalidCallback){return}for(let elementName in mixinEntry.dependants){if(elementName!==this._currentElement){invalidCallback(elementName)}}}_produceCssProperties(matchText,propertyName,valueProperty,valueMixin,rule){if(valueProperty){processVariableAndFallback(valueProperty,(prefix,value)=>{if(value&&this._map.get(value)){valueMixin=`@apply ${value};`}})}if(!valueMixin){return matchText}let mixinAsProperties=this._consumeCssProperties(""+valueMixin,rule);let prefix=matchText.slice(0,matchText.indexOf("--"));let mixinValues=this._cssTextToMap(mixinAsProperties,true);let combinedProps=mixinValues;let mixinEntry=this._map.get(propertyName);let oldProps=mixinEntry&&mixinEntry.properties;if(oldProps){combinedProps=Object.assign(Object.create(oldProps),mixinValues)}else{this._map.set(propertyName,combinedProps)}let out=[];let p,v;let needToInvalidate=false;for(p in combinedProps){v=mixinValues[p];if(v===undefined){v="initial"}if(oldProps&&!(p in oldProps)){needToInvalidate=true}out.push(`${propertyName}${MIXIN_VAR_SEP}${p}: ${v}`)}if(needToInvalidate){this._invalidateMixinEntry(mixinEntry)}if(mixinEntry){mixinEntry.properties=combinedProps}if(valueProperty){prefix=`${matchText};${prefix}`}return`${prefix}${out.join("; ")};`}}ApplyShim.prototype["detectMixin"]=ApplyShim.prototype.detectMixin;ApplyShim.prototype["transformStyle"]=ApplyShim.prototype.transformStyle;ApplyShim.prototype["transformCustomStyle"]=ApplyShim.prototype.transformCustomStyle;ApplyShim.prototype["transformRules"]=ApplyShim.prototype.transformRules;ApplyShim.prototype["transformRule"]=ApplyShim.prototype.transformRule;ApplyShim.prototype["transformTemplate"]=ApplyShim.prototype.transformTemplate;ApplyShim.prototype["_separator"]=MIXIN_VAR_SEP;Object.defineProperty(ApplyShim.prototype,"invalidCallback",{get(){return invalidCallback},set(cb){invalidCallback=cb}});const templateMap={};const CURRENT_VERSION="_applyShimCurrentVersion";const NEXT_VERSION="_applyShimNextVersion";const VALIDATING_VERSION="_applyShimValidatingVersion";const promise=Promise.resolve();function invalidate(elementName){let template=templateMap[elementName];if(template){invalidateTemplate(template)}}function invalidateTemplate(template){template[CURRENT_VERSION]=template[CURRENT_VERSION]||0;template[VALIDATING_VERSION]=template[VALIDATING_VERSION]||0;template[NEXT_VERSION]=(template[NEXT_VERSION]||0)+1}function templateIsValid(template){return template[CURRENT_VERSION]===template[NEXT_VERSION]}function templateIsValidating(template){return!templateIsValid(template)&&template[VALIDATING_VERSION]===template[NEXT_VERSION]}function startValidatingTemplate(template){template[VALIDATING_VERSION]=template[NEXT_VERSION];if(!template._validating){template._validating=true;promise.then(function(){template[CURRENT_VERSION]=template[NEXT_VERSION];template._validating=false})}}let readyPromise=null;let whenReady=window["HTMLImports"]&&window["HTMLImports"]["whenReady"]||null;let resolveFn;function documentWait(callback){requestAnimationFrame(function(){if(whenReady){whenReady(callback)}else{if(!readyPromise){readyPromise=new Promise(resolve=>{resolveFn=resolve});if(document.readyState==="complete"){resolveFn()}else{document.addEventListener("readystatechange",()=>{if(document.readyState==="complete"){resolveFn()}})}}readyPromise.then(function(){callback&&callback()})}})}const SEEN_MARKER="__seenByShadyCSS";const CACHED_STYLE="__shadyCSSCachedStyle";let transformFn=null;let validateFn=null;class CustomStyleInterface{constructor(){this["customStyles"]=[];this["enqueued"]=false;documentWait(()=>{if(window["ShadyCSS"]["flushCustomStyles"]){window["ShadyCSS"]["flushCustomStyles"]()}})}enqueueDocumentValidation(){if(this["enqueued"]||!validateFn){return}this["enqueued"]=true;documentWait(validateFn)}addCustomStyle(style){if(!style[SEEN_MARKER]){style[SEEN_MARKER]=true;this["customStyles"].push(style);this.enqueueDocumentValidation()}}getStyleForCustomStyle(customStyle){if(customStyle[CACHED_STYLE]){return customStyle[CACHED_STYLE]}let style;if(customStyle["getStyle"]){style=customStyle["getStyle"]()}else{style=customStyle}return style}processStyles(){const cs=this["customStyles"];for(let i=0;i<cs.length;i++){const customStyle=cs[i];if(customStyle[CACHED_STYLE]){continue}const style=this.getStyleForCustomStyle(customStyle);if(style){const styleToTransform=style["__appliedElement"]||style;if(transformFn){transformFn(styleToTransform)}customStyle[CACHED_STYLE]=styleToTransform}}return cs}}CustomStyleInterface.prototype["addCustomStyle"]=CustomStyleInterface.prototype.addCustomStyle;CustomStyleInterface.prototype["getStyleForCustomStyle"]=CustomStyleInterface.prototype.getStyleForCustomStyle;CustomStyleInterface.prototype["processStyles"]=CustomStyleInterface.prototype.processStyles;Object.defineProperties(CustomStyleInterface.prototype,{transformCallback:{get(){return transformFn},set(fn){transformFn=fn}},validateCallback:{get(){return validateFn},set(fn){let needsEnqueue=false;if(!validateFn){needsEnqueue=true}validateFn=fn;if(needsEnqueue){this.enqueueDocumentValidation()}}}});const applyShim=new ApplyShim;class ApplyShimInterface{constructor(){this.customStyleInterface=null;applyShim["invalidCallback"]=invalidate}ensure(){if(this.customStyleInterface){return}if(window.ShadyCSS.CustomStyleInterface){this.customStyleInterface=window.ShadyCSS.CustomStyleInterface;this.customStyleInterface["transformCallback"]=(style=>{applyShim.transformCustomStyle(style)});this.customStyleInterface["validateCallback"]=(()=>{requestAnimationFrame(()=>{if(this.customStyleInterface["enqueued"]){this.flushCustomStyles()}})})}}prepareTemplate(template,elementName){this.ensure();if(elementHasBuiltCss(template)){return}templateMap[elementName]=template;let ast=applyShim.transformTemplate(template,elementName);template["_styleAst"]=ast}flushCustomStyles(){this.ensure();if(!this.customStyleInterface){return}let styles=this.customStyleInterface["processStyles"]();if(!this.customStyleInterface["enqueued"]){return}for(let i=0;i<styles.length;i++){let cs=styles[i];let style=this.customStyleInterface["getStyleForCustomStyle"](cs);if(style){applyShim.transformCustomStyle(style)}}this.customStyleInterface["enqueued"]=false}styleSubtree(element,properties){this.ensure();if(properties){updateNativeProperties(element,properties)}if(element.shadowRoot){this.styleElement(element);let shadowChildren=element.shadowRoot.children||element.shadowRoot.childNodes;for(let i=0;i<shadowChildren.length;i++){this.styleSubtree(shadowChildren[i])}}else{let children=element.children||element.childNodes;for(let i=0;i<children.length;i++){this.styleSubtree(children[i])}}}styleElement(element){this.ensure();let{is:is}=getIsExtends(element);let template=templateMap[is];if(template&&elementHasBuiltCss(template)){return}if(template&&!templateIsValid(template)){if(!templateIsValidating(template)){this.prepareTemplate(template,is);startValidatingTemplate(template)}let root=element.shadowRoot;if(root){let style=root.querySelector("style");if(style){style["__cssRules"]=template["_styleAst"];style.textContent=toCssText(template["_styleAst"])}}}}styleDocument(properties){this.ensure();this.styleSubtree(document.body,properties)}}if(!window.ShadyCSS||!window.ShadyCSS.ScopingShim){const applyShimInterface=new ApplyShimInterface;let CustomStyleInterface=window.ShadyCSS&&window.ShadyCSS.CustomStyleInterface;window.ShadyCSS={prepareTemplate(template,elementName,elementExtends){applyShimInterface.flushCustomStyles();applyShimInterface.prepareTemplate(template,elementName)},prepareTemplateStyles(template,elementName,elementExtends){window.ShadyCSS.prepareTemplate(template,elementName,elementExtends)},prepareTemplateDom(template,elementName){},styleSubtree(element,properties){applyShimInterface.flushCustomStyles();applyShimInterface.styleSubtree(element,properties)},styleElement(element){applyShimInterface.flushCustomStyles();applyShimInterface.styleElement(element)},styleDocument(properties){applyShimInterface.flushCustomStyles();applyShimInterface.styleDocument(properties)},getComputedStyleValue(element,property){return getComputedStyleValue(element,property)},flushCustomStyles(){applyShimInterface.flushCustomStyles()},nativeCss:nativeCssVariables,nativeShadow:nativeShadow,cssBuild:cssBuild,disableRuntime:disableRuntime};if(CustomStyleInterface){window.ShadyCSS.CustomStyleInterface=CustomStyleInterface}}window.ShadyCSS.ApplyShim=applyShim;let modules={};let lcModules={};function setModule(id,module){modules[id]=lcModules[id.toLowerCase()]=module}function findModule(id){return modules[id]||lcModules[id.toLowerCase()]}function styleOutsideTemplateCheck(inst){if(inst.querySelector("style")){console.warn("dom-module %s has style outside template",inst.id)}}class DomModule extends HTMLElement{static get observedAttributes(){return["id"]}static import(id,selector){if(id){let m=findModule(id);if(m&&selector){return m.querySelector(selector)}return m}return null}attributeChangedCallback(name,old,value,namespace){if(old!==value){this.register()}}get assetpath(){if(!this.__assetpath){const owner=window.HTMLImports&&HTMLImports.importForElement?HTMLImports.importForElement(this)||document:this.ownerDocument;const url=resolveUrl(this.getAttribute("assetpath")||"",owner.baseURI);this.__assetpath=pathFromUrl(url)}return this.__assetpath}register(id){id=id||this.id;if(id){if(strictTemplatePolicy&&findModule(id)!==undefined){setModule(id,null);throw new Error(`strictTemplatePolicy: dom-module ${id} re-registered`)}this.id=id;setModule(id,this);styleOutsideTemplateCheck(this)}}}DomModule.prototype["modules"]=modules;customElements.define("dom-module",DomModule);const MODULE_STYLE_LINK_SELECTOR="link[rel=import][type~=css]";const INCLUDE_ATTR="include";const SHADY_UNSCOPED_ATTR="shady-unscoped";function importModule(moduleId){return DomModule.import(moduleId)}function styleForImport(importDoc){let container=importDoc.body?importDoc.body:importDoc;const importCss=resolveCss(container.textContent,importDoc.baseURI);const style=document.createElement("style");style.textContent=importCss;return style}function stylesFromModules(moduleIds){const modules=moduleIds.trim().split(/\s+/);const styles=[];for(let i=0;i<modules.length;i++){styles.push(...stylesFromModule(modules[i]))}return styles}function stylesFromModule(moduleId){const m=importModule(moduleId);if(!m){console.warn("Could not find style data in module named",moduleId);return[]}if(m._styles===undefined){const styles=[];styles.push(..._stylesFromModuleImports(m));const template=m.querySelector("template");if(template){styles.push(...stylesFromTemplate(template,m.assetpath))}m._styles=styles}return m._styles}function stylesFromTemplate(template,baseURI){if(!template._styles){const styles=[];const e$=template.content.querySelectorAll("style");for(let i=0;i<e$.length;i++){let e=e$[i];let include=e.getAttribute(INCLUDE_ATTR);if(include){styles.push(...stylesFromModules(include).filter(function(item,index,self){return self.indexOf(item)===index}))}if(baseURI){e.textContent=resolveCss(e.textContent,baseURI)}styles.push(e)}template._styles=styles}return template._styles}function stylesFromModuleImports(moduleId){let m=importModule(moduleId);return m?_stylesFromModuleImports(m):[]}function _stylesFromModuleImports(module){const styles=[];const p$=module.querySelectorAll(MODULE_STYLE_LINK_SELECTOR);for(let i=0;i<p$.length;i++){let p=p$[i];if(p.import){const importDoc=p.import;const unscoped=p.hasAttribute(SHADY_UNSCOPED_ATTR);if(unscoped&&!importDoc._unscopedStyle){const style=styleForImport(importDoc);style.setAttribute(SHADY_UNSCOPED_ATTR,"");importDoc._unscopedStyle=style}else if(!importDoc._style){importDoc._style=styleForImport(importDoc)}styles.push(unscoped?importDoc._unscopedStyle:importDoc._style)}}return styles}function cssFromModules(moduleIds){let modules=moduleIds.trim().split(/\s+/);let cssText="";for(let i=0;i<modules.length;i++){cssText+=cssFromModule(modules[i])}return cssText}function cssFromModule(moduleId){let m=importModule(moduleId);if(m&&m._cssText===undefined){let cssText=_cssFromModuleImports(m);let t=m.querySelector("template");if(t){cssText+=cssFromTemplate(t,m.assetpath)}m._cssText=cssText||null}if(!m){console.warn("Could not find style data in module named",moduleId)}return m&&m._cssText||""}function cssFromTemplate(template,baseURI){let cssText="";const e$=stylesFromTemplate(template,baseURI);for(let i=0;i<e$.length;i++){let e=e$[i];if(e.parentNode){e.parentNode.removeChild(e)}cssText+=e.textContent}return cssText}function _cssFromModuleImports(module){let cssText="";let styles=_stylesFromModuleImports(module);for(let i=0;i<styles.length;i++){cssText+=styles[i].textContent}return cssText}function isPath(path){return path.indexOf(".")>=0}function root(path){let dotIndex=path.indexOf(".");if(dotIndex===-1){return path}return path.slice(0,dotIndex)}function isAncestor(base,path){return base.indexOf(path+".")===0}function isDescendant(base,path){return path.indexOf(base+".")===0}function translate(base,newBase,path){return newBase+path.slice(base.length)}function matches(base,path){return base===path||isAncestor(base,path)||isDescendant(base,path)}function normalize(path){if(Array.isArray(path)){let parts=[];for(let i=0;i<path.length;i++){let args=path[i].toString().split(".");for(let j=0;j<args.length;j++){parts.push(args[j])}}return parts.join(".")}else{return path}}function split(path){if(Array.isArray(path)){return normalize(path).split(".")}return path.toString().split(".")}function get(root,path,info){let prop=root;let parts=split(path);for(let i=0;i<parts.length;i++){if(!prop){return}let part=parts[i];prop=prop[part]}if(info){info.path=parts.join(".")}return prop}function set(root,path,value){let prop=root;let parts=split(path);let last=parts[parts.length-1];if(parts.length>1){for(let i=0;i<parts.length-1;i++){let part=parts[i];prop=prop[part];if(!prop){return}}prop[last]=value}else{prop[path]=value}return parts.join(".")}const caseMap={};const DASH_TO_CAMEL=/-[a-z]/g;const CAMEL_TO_DASH=/([A-Z])/g;function dashToCamelCase(dash){return caseMap[dash]||(caseMap[dash]=dash.indexOf("-")<0?dash:dash.replace(DASH_TO_CAMEL,m=>m[1].toUpperCase()))}function camelToDashCase(camel){return caseMap[camel]||(caseMap[camel]=camel.replace(CAMEL_TO_DASH,"-$1").toLowerCase())}const microtask=microTask;const PropertiesChanged=dedupingMixin(superClass=>{class PropertiesChanged extends superClass{static createProperties(props){const proto=this.prototype;for(let prop in props){if(!(prop in proto)){proto._createPropertyAccessor(prop)}}}static attributeNameForProperty(property){return property.toLowerCase()}static typeForProperty(name){}_createPropertyAccessor(property,readOnly){this._addPropertyToAttributeMap(property);if(!this.hasOwnProperty("__dataHasAccessor")){this.__dataHasAccessor=Object.assign({},this.__dataHasAccessor)}if(!this.__dataHasAccessor[property]){this.__dataHasAccessor[property]=true;this._definePropertyAccessor(property,readOnly)}}_addPropertyToAttributeMap(property){if(!this.hasOwnProperty("__dataAttributes")){this.__dataAttributes=Object.assign({},this.__dataAttributes)}if(!this.__dataAttributes[property]){const attr=this.constructor.attributeNameForProperty(property);this.__dataAttributes[attr]=property}}_definePropertyAccessor(property,readOnly){Object.defineProperty(this,property,{get(){return this._getProperty(property)},set:readOnly?function(){}:function(value){this._setProperty(property,value)}})}constructor(){super();this.__dataEnabled=false;this.__dataReady=false;this.__dataInvalid=false;this.__data={};this.__dataPending=null;this.__dataOld=null;this.__dataInstanceProps=null;this.__serializing=false;this._initializeProperties()}ready(){this.__dataReady=true;this._flushProperties()}_initializeProperties(){for(let p in this.__dataHasAccessor){if(this.hasOwnProperty(p)){this.__dataInstanceProps=this.__dataInstanceProps||{};this.__dataInstanceProps[p]=this[p];delete this[p]}}}_initializeInstanceProperties(props){Object.assign(this,props)}_setProperty(property,value){if(this._setPendingProperty(property,value)){this._invalidateProperties()}}_getProperty(property){return this.__data[property]}_setPendingProperty(property,value,ext){let old=this.__data[property];let changed=this._shouldPropertyChange(property,value,old);if(changed){if(!this.__dataPending){this.__dataPending={};this.__dataOld={}}if(this.__dataOld&&!(property in this.__dataOld)){this.__dataOld[property]=old}this.__data[property]=value;this.__dataPending[property]=value}return changed}_invalidateProperties(){if(!this.__dataInvalid&&this.__dataReady){this.__dataInvalid=true;microtask.run(()=>{if(this.__dataInvalid){this.__dataInvalid=false;this._flushProperties()}})}}_enableProperties(){if(!this.__dataEnabled){this.__dataEnabled=true;if(this.__dataInstanceProps){this._initializeInstanceProperties(this.__dataInstanceProps);this.__dataInstanceProps=null}this.ready()}}_flushProperties(){const props=this.__data;const changedProps=this.__dataPending;const old=this.__dataOld;if(this._shouldPropertiesChange(props,changedProps,old)){this.__dataPending=null;this.__dataOld=null;this._propertiesChanged(props,changedProps,old)}}_shouldPropertiesChange(currentProps,changedProps,oldProps){return Boolean(changedProps)}_propertiesChanged(currentProps,changedProps,oldProps){}_shouldPropertyChange(property,value,old){return old!==value&&(old===old||value===value)}attributeChangedCallback(name,old,value,namespace){if(old!==value){this._attributeToProperty(name,value)}if(super.attributeChangedCallback){super.attributeChangedCallback(name,old,value,namespace)}}_attributeToProperty(attribute,value,type){if(!this.__serializing){const map=this.__dataAttributes;const property=map&&map[attribute]||attribute;this[property]=this._deserializeValue(value,type||this.constructor.typeForProperty(property))}}_propertyToAttribute(property,attribute,value){this.__serializing=true;value=arguments.length<3?this[property]:value;this._valueToNodeAttribute(this,value,attribute||this.constructor.attributeNameForProperty(property));this.__serializing=false}_valueToNodeAttribute(node,value,attribute){const str=this._serializeValue(value);if(str===undefined){node.removeAttribute(attribute)}else{if(attribute==="class"||attribute==="name"||attribute==="slot"){node=wrap(node)}node.setAttribute(attribute,str)}}_serializeValue(value){switch(typeof value){case"boolean":return value?"":undefined;default:return value!=null?value.toString():undefined}}_deserializeValue(value,type){switch(type){case Boolean:return value!==null;case Number:return Number(value);default:return value}}}return PropertiesChanged});const nativeProperties={};let proto=HTMLElement.prototype;while(proto){let props=Object.getOwnPropertyNames(proto);for(let i=0;i<props.length;i++){nativeProperties[props[i]]=true}proto=Object.getPrototypeOf(proto)}function saveAccessorValue(model,property){if(!nativeProperties[property]){let value=model[property];if(value!==undefined){if(model.__data){model._setPendingProperty(property,value)}else{if(!model.__dataProto){model.__dataProto={}}else if(!model.hasOwnProperty(JSCompiler_renameProperty("__dataProto",model))){model.__dataProto=Object.create(model.__dataProto)}model.__dataProto[property]=value}}}}const PropertyAccessors=dedupingMixin(superClass=>{const base=PropertiesChanged(superClass);class PropertyAccessors extends base{static createPropertiesForAttributes(){let a$=this.observedAttributes;for(let i=0;i<a$.length;i++){this.prototype._createPropertyAccessor(dashToCamelCase(a$[i]))}}static attributeNameForProperty(property){return camelToDashCase(property)}_initializeProperties(){if(this.__dataProto){this._initializeProtoProperties(this.__dataProto);this.__dataProto=null}super._initializeProperties()}_initializeProtoProperties(props){for(let p in props){this._setProperty(p,props[p])}}_ensureAttribute(attribute,value){const el=this;if(!el.hasAttribute(attribute)){this._valueToNodeAttribute(el,value,attribute)}}_serializeValue(value){switch(typeof value){case"object":if(value instanceof Date){return value.toString()}else if(value){try{return JSON.stringify(value)}catch(x){return""}}default:return super._serializeValue(value)}}_deserializeValue(value,type){let outValue;switch(type){case Object:try{outValue=JSON.parse(value)}catch(x){outValue=value}break;case Array:try{outValue=JSON.parse(value)}catch(x){outValue=null;console.warn(`Polymer::Attributes: couldn't decode Array as JSON: ${value}`)}break;case Date:outValue=isNaN(value)?String(value):Number(value);outValue=new Date(outValue);break;default:outValue=super._deserializeValue(value,type);break}return outValue}_definePropertyAccessor(property,readOnly){saveAccessorValue(this,property);super._definePropertyAccessor(property,readOnly)}_hasAccessor(property){return this.__dataHasAccessor&&this.__dataHasAccessor[property]}_isPropertyPending(prop){return Boolean(this.__dataPending&&prop in this.__dataPending)}}return PropertyAccessors});const walker=document.createTreeWalker(document,NodeFilter.SHOW_ALL,null,false);const templateExtensions={"dom-if":true,"dom-repeat":true};function wrapTemplateExtension(node){let is=node.getAttribute("is");if(is&&templateExtensions[is]){let t=node;t.removeAttribute("is");node=t.ownerDocument.createElement(is);t.parentNode.replaceChild(node,t);node.appendChild(t);while(t.attributes.length){node.setAttribute(t.attributes[0].name,t.attributes[0].value);t.removeAttribute(t.attributes[0].name)}}return node}function findTemplateNode(root,nodeInfo){let parent=nodeInfo.parentInfo&&findTemplateNode(root,nodeInfo.parentInfo);if(parent){walker.currentNode=parent;for(let n=walker.firstChild(),i=0;n;n=walker.nextSibling()){if(nodeInfo.parentIndex===i++){return n}}}else{return root}}function applyIdToMap(inst,map,node,nodeInfo){if(nodeInfo.id){map[nodeInfo.id]=node}}function applyEventListener(inst,node,nodeInfo){if(nodeInfo.events&&nodeInfo.events.length){for(let j=0,e$=nodeInfo.events,e;j<e$.length&&(e=e$[j]);j++){inst._addMethodEventListenerToNode(node,e.name,e.value,inst)}}}function applyTemplateContent(inst,node,nodeInfo){if(nodeInfo.templateInfo){node._templateInfo=nodeInfo.templateInfo}}function createNodeEventHandler(context,eventName,methodName){context=context._methodHost||context;let handler=function(e){if(context[methodName]){context[methodName](e,e.detail)}else{console.warn("listener method `"+methodName+"` not defined")}};return handler}const TemplateStamp=dedupingMixin(superClass=>{class TemplateStamp extends superClass{static _parseTemplate(template,outerTemplateInfo){if(!template._templateInfo){let templateInfo=template._templateInfo={};templateInfo.nodeInfoList=[];templateInfo.stripWhiteSpace=outerTemplateInfo&&outerTemplateInfo.stripWhiteSpace||template.hasAttribute("strip-whitespace");this._parseTemplateContent(template,templateInfo,{parent:null})}return template._templateInfo}static _parseTemplateContent(template,templateInfo,nodeInfo){return this._parseTemplateNode(template.content,templateInfo,nodeInfo)}static _parseTemplateNode(node,templateInfo,nodeInfo){let noted;let element=node;if(element.localName=="template"&&!element.hasAttribute("preserve-content")){noted=this._parseTemplateNestedTemplate(element,templateInfo,nodeInfo)||noted}else if(element.localName==="slot"){templateInfo.hasInsertionPoint=true}walker.currentNode=element;if(walker.firstChild()){noted=this._parseTemplateChildNodes(element,templateInfo,nodeInfo)||noted}if(element.hasAttributes&&element.hasAttributes()){noted=this._parseTemplateNodeAttributes(element,templateInfo,nodeInfo)||noted}return noted}static _parseTemplateChildNodes(root,templateInfo,nodeInfo){if(root.localName==="script"||root.localName==="style"){return}walker.currentNode=root;for(let node=walker.firstChild(),parentIndex=0,next;node;node=next){if(node.localName=="template"){node=wrapTemplateExtension(node)}walker.currentNode=node;next=walker.nextSibling();if(node.nodeType===Node.TEXT_NODE){let n=next;while(n&&n.nodeType===Node.TEXT_NODE){node.textContent+=n.textContent;next=walker.nextSibling();root.removeChild(n);n=next}if(templateInfo.stripWhiteSpace&&!node.textContent.trim()){root.removeChild(node);continue}}let childInfo={parentIndex:parentIndex,parentInfo:nodeInfo};if(this._parseTemplateNode(node,templateInfo,childInfo)){childInfo.infoIndex=templateInfo.nodeInfoList.push(childInfo)-1}walker.currentNode=node;if(walker.parentNode()){parentIndex++}}}static _parseTemplateNestedTemplate(node,outerTemplateInfo,nodeInfo){let templateInfo=this._parseTemplate(node,outerTemplateInfo);let content=templateInfo.content=node.content.ownerDocument.createDocumentFragment();content.appendChild(node.content);nodeInfo.templateInfo=templateInfo;return true}static _parseTemplateNodeAttributes(node,templateInfo,nodeInfo){let noted=false;let attrs=Array.from(node.attributes);for(let i=attrs.length-1,a;a=attrs[i];i--){noted=this._parseTemplateNodeAttribute(node,templateInfo,nodeInfo,a.name,a.value)||noted}return noted}static _parseTemplateNodeAttribute(node,templateInfo,nodeInfo,name,value){if(name.slice(0,3)==="on-"){node.removeAttribute(name);nodeInfo.events=nodeInfo.events||[];nodeInfo.events.push({name:name.slice(3),value:value});return true}else if(name==="id"){nodeInfo.id=value;return true}return false}static _contentForTemplate(template){let templateInfo=template._templateInfo;return templateInfo&&templateInfo.content||template.content}_stampTemplate(template){if(template&&!template.content&&window.HTMLTemplateElement&&HTMLTemplateElement.decorate){HTMLTemplateElement.decorate(template)}let templateInfo=this.constructor._parseTemplate(template);let nodeInfo=templateInfo.nodeInfoList;let content=templateInfo.content||template.content;let dom=document.importNode(content,true);dom.__noInsertionPoint=!templateInfo.hasInsertionPoint;let nodes=dom.nodeList=new Array(nodeInfo.length);dom.$={};for(let i=0,l=nodeInfo.length,info;i<l&&(info=nodeInfo[i]);i++){let node=nodes[i]=findTemplateNode(dom,info);applyIdToMap(this,dom.$,node,info);applyTemplateContent(this,node,info);applyEventListener(this,node,info)}dom=dom;return dom}_addMethodEventListenerToNode(node,eventName,methodName,context){context=context||node;let handler=createNodeEventHandler(context,eventName,methodName);this._addEventListenerToNode(node,eventName,handler);return handler}_addEventListenerToNode(node,eventName,handler){node.addEventListener(eventName,handler)}_removeEventListenerFromNode(node,eventName,handler){node.removeEventListener(eventName,handler)}}return TemplateStamp});let dedupeId$1=0;const TYPES={COMPUTE:"__computeEffects",REFLECT:"__reflectEffects",NOTIFY:"__notifyEffects",PROPAGATE:"__propagateEffects",OBSERVE:"__observeEffects",READ_ONLY:"__readOnly"};const capitalAttributeRegex=/[A-Z]/;function ensureOwnEffectMap(model,type){let effects=model[type];if(!effects){effects=model[type]={}}else if(!model.hasOwnProperty(type)){effects=model[type]=Object.create(model[type]);for(let p in effects){let protoFx=effects[p];let instFx=effects[p]=Array(protoFx.length);for(let i=0;i<protoFx.length;i++){instFx[i]=protoFx[i]}}}return effects}function runEffects(inst,effects,props,oldProps,hasPaths,extraArgs){if(effects){let ran=false;let id=dedupeId$1++;for(let prop in props){if(runEffectsForProperty(inst,effects,id,prop,props,oldProps,hasPaths,extraArgs)){ran=true}}return ran}return false}function runEffectsForProperty(inst,effects,dedupeId,prop,props,oldProps,hasPaths,extraArgs){let ran=false;let rootProperty=hasPaths?root(prop):prop;let fxs=effects[rootProperty];if(fxs){for(let i=0,l=fxs.length,fx;i<l&&(fx=fxs[i]);i++){if((!fx.info||fx.info.lastRun!==dedupeId)&&(!hasPaths||pathMatchesTrigger(prop,fx.trigger))){if(fx.info){fx.info.lastRun=dedupeId}fx.fn(inst,prop,props,oldProps,fx.info,hasPaths,extraArgs);ran=true}}}return ran}function pathMatchesTrigger(path,trigger){if(trigger){let triggerPath=trigger.name;return triggerPath==path||!!(trigger.structured&&isAncestor(triggerPath,path))||!!(trigger.wildcard&&isDescendant(triggerPath,path))}else{return true}}function runObserverEffect(inst,property,props,oldProps,info){let fn=typeof info.method==="string"?inst[info.method]:info.method;let changedProp=info.property;if(fn){fn.call(inst,inst.__data[changedProp],oldProps[changedProp])}else if(!info.dynamicFn){console.warn("observer method `"+info.method+"` not defined")}}function runNotifyEffects(inst,notifyProps,props,oldProps,hasPaths){let fxs=inst[TYPES.NOTIFY];let notified;let id=dedupeId$1++;for(let prop in notifyProps){if(notifyProps[prop]){if(fxs&&runEffectsForProperty(inst,fxs,id,prop,props,oldProps,hasPaths)){notified=true}else if(hasPaths&¬ifyPath(inst,prop,props)){notified=true}}}let host;if(notified&&(host=inst.__dataHost)&&host._invalidateProperties){host._invalidateProperties()}}function notifyPath(inst,path,props){let rootProperty=root(path);if(rootProperty!==path){let eventName=camelToDashCase(rootProperty)+"-changed";dispatchNotifyEvent(inst,eventName,props[path],path);return true}return false}function dispatchNotifyEvent(inst,eventName,value,path){let detail={value:value,queueProperty:true};if(path){detail.path=path}wrap(inst).dispatchEvent(new CustomEvent(eventName,{detail:detail}))}function runNotifyEffect(inst,property,props,oldProps,info,hasPaths){let rootProperty=hasPaths?root(property):property;let path=rootProperty!=property?property:null;let value=path?get(inst,path):inst.__data[property];if(path&&value===undefined){value=props[property]}dispatchNotifyEvent(inst,info.eventName,value,path)}function handleNotification(event,inst,fromProp,toPath,negate){let value;let detail=event.detail;let fromPath=detail&&detail.path;if(fromPath){toPath=translate(fromProp,toPath,fromPath);value=detail&&detail.value}else{value=event.currentTarget[fromProp]}value=negate?!value:value;if(!inst[TYPES.READ_ONLY]||!inst[TYPES.READ_ONLY][toPath]){if(inst._setPendingPropertyOrPath(toPath,value,true,Boolean(fromPath))&&(!detail||!detail.queueProperty)){inst._invalidateProperties()}}}function runReflectEffect(inst,property,props,oldProps,info){let value=inst.__data[property];if(sanitizeDOMValue){value=sanitizeDOMValue(value,info.attrName,"attribute",inst)}inst._propertyToAttribute(property,info.attrName,value)}function runComputedEffects(inst,changedProps,oldProps,hasPaths){let computeEffects=inst[TYPES.COMPUTE];if(computeEffects){let inputProps=changedProps;while(runEffects(inst,computeEffects,inputProps,oldProps,hasPaths)){Object.assign(oldProps,inst.__dataOld);Object.assign(changedProps,inst.__dataPending);inputProps=inst.__dataPending;inst.__dataPending=null}}}function runComputedEffect(inst,property,props,oldProps,info){let result=runMethodEffect(inst,property,props,oldProps,info);let computedProp=info.methodInfo;if(inst.__dataHasAccessor&&inst.__dataHasAccessor[computedProp]){inst._setPendingProperty(computedProp,result,true)}else{inst[computedProp]=result}}function computeLinkedPaths(inst,path,value){let links=inst.__dataLinkedPaths;if(links){let link;for(let a in links){let b=links[a];if(isDescendant(a,path)){link=translate(a,b,path);inst._setPendingPropertyOrPath(link,value,true,true)}else if(isDescendant(b,path)){link=translate(b,a,path);inst._setPendingPropertyOrPath(link,value,true,true)}}}}function addBinding(constructor,templateInfo,nodeInfo,kind,target,parts,literal){nodeInfo.bindings=nodeInfo.bindings||[];let binding={kind:kind,target:target,parts:parts,literal:literal,isCompound:parts.length!==1};nodeInfo.bindings.push(binding);if(shouldAddListener(binding)){let{event:event,negate:negate}=binding.parts[0];binding.listenerEvent=event||camelToDashCase(target)+"-changed";binding.listenerNegate=negate}let index=templateInfo.nodeInfoList.length;for(let i=0;i<binding.parts.length;i++){let part=binding.parts[i];part.compoundIndex=i;addEffectForBindingPart(constructor,templateInfo,binding,part,index)}}function addEffectForBindingPart(constructor,templateInfo,binding,part,index){if(!part.literal){if(binding.kind==="attribute"&&binding.target[0]==="-"){console.warn("Cannot set attribute "+binding.target+' because "-" is not a valid attribute starting character')}else{let dependencies=part.dependencies;let info={index:index,binding:binding,part:part,evaluator:constructor};for(let j=0;j<dependencies.length;j++){let trigger=dependencies[j];if(typeof trigger=="string"){trigger=parseArg(trigger);trigger.wildcard=true}constructor._addTemplatePropertyEffect(templateInfo,trigger.rootProperty,{fn:runBindingEffect,info:info,trigger:trigger})}}}}function runBindingEffect(inst,path,props,oldProps,info,hasPaths,nodeList){let node=nodeList[info.index];let binding=info.binding;let part=info.part;if(hasPaths&&part.source&&path.length>part.source.length&&binding.kind=="property"&&!binding.isCompound&&node.__isPropertyEffectsClient&&node.__dataHasAccessor&&node.__dataHasAccessor[binding.target]){let value=props[path];path=translate(part.source,binding.target,path);if(node._setPendingPropertyOrPath(path,value,false,true)){inst._enqueueClient(node)}}else{let value=info.evaluator._evaluateBinding(inst,part,path,props,oldProps,hasPaths);applyBindingValue(inst,node,binding,part,value)}}function applyBindingValue(inst,node,binding,part,value){value=computeBindingValue(node,value,binding,part);if(sanitizeDOMValue){value=sanitizeDOMValue(value,binding.target,binding.kind,node)}if(binding.kind=="attribute"){inst._valueToNodeAttribute(node,value,binding.target)}else{let prop=binding.target;if(node.__isPropertyEffectsClient&&node.__dataHasAccessor&&node.__dataHasAccessor[prop]){if(!node[TYPES.READ_ONLY]||!node[TYPES.READ_ONLY][prop]){if(node._setPendingProperty(prop,value)){inst._enqueueClient(node)}}}else{inst._setUnmanagedPropertyToNode(node,prop,value)}}}function computeBindingValue(node,value,binding,part){if(binding.isCompound){let storage=node.__dataCompoundStorage[binding.target];storage[part.compoundIndex]=value;value=storage.join("")}if(binding.kind!=="attribute"){if(binding.target==="textContent"||binding.target==="value"&&(node.localName==="input"||node.localName==="textarea")){value=value==undefined?"":value}}return value}function shouldAddListener(binding){return Boolean(binding.target)&&binding.kind!="attribute"&&binding.kind!="text"&&!binding.isCompound&&binding.parts[0].mode==="{"}function setupBindings(inst,templateInfo){let{nodeList:nodeList,nodeInfoList:nodeInfoList}=templateInfo;if(nodeInfoList.length){for(let i=0;i<nodeInfoList.length;i++){let info=nodeInfoList[i];let node=nodeList[i];let bindings=info.bindings;if(bindings){for(let i=0;i<bindings.length;i++){let binding=bindings[i];setupCompoundStorage(node,binding);addNotifyListener(node,inst,binding)}}node.__dataHost=inst}}}function setupCompoundStorage(node,binding){if(binding.isCompound){let storage=node.__dataCompoundStorage||(node.__dataCompoundStorage={});let parts=binding.parts;let literals=new Array(parts.length);for(let j=0;j<parts.length;j++){literals[j]=parts[j].literal}let target=binding.target;storage[target]=literals;if(binding.literal&&binding.kind=="property"){node[target]=binding.literal}}}function addNotifyListener(node,inst,binding){if(binding.listenerEvent){let part=binding.parts[0];node.addEventListener(binding.listenerEvent,function(e){handleNotification(e,inst,binding.target,part.source,part.negate)})}}function createMethodEffect(model,sig,type,effectFn,methodInfo,dynamicFn){dynamicFn=sig.static||dynamicFn&&(typeof dynamicFn!=="object"||dynamicFn[sig.methodName]);let info={methodName:sig.methodName,args:sig.args,methodInfo:methodInfo,dynamicFn:dynamicFn};for(let i=0,arg;i<sig.args.length&&(arg=sig.args[i]);i++){if(!arg.literal){model._addPropertyEffect(arg.rootProperty,type,{fn:effectFn,info:info,trigger:arg})}}if(dynamicFn){model._addPropertyEffect(sig.methodName,type,{fn:effectFn,info:info})}}function runMethodEffect(inst,property,props,oldProps,info){let context=inst._methodHost||inst;let fn=context[info.methodName];if(fn){let args=inst._marshalArgs(info.args,property,props);return fn.apply(context,args)}else if(!info.dynamicFn){console.warn("method `"+info.methodName+"` not defined")}}const emptyArray=[];const IDENT="(?:"+"[a-zA-Z_$][\\w.:$\\-*]*"+")";const NUMBER="(?:"+"[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?"+")";const SQUOTE_STRING="(?:"+"'(?:[^'\\\\]|\\\\.)*'"+")";const DQUOTE_STRING="(?:"+'"(?:[^"\\\\]|\\\\.)*"'+")";const STRING="(?:"+SQUOTE_STRING+"|"+DQUOTE_STRING+")";const ARGUMENT="(?:("+IDENT+"|"+NUMBER+"|"+STRING+")\\s*"+")";const ARGUMENTS="(?:"+ARGUMENT+"(?:,\\s*"+ARGUMENT+")*"+")";const ARGUMENT_LIST="(?:"+"\\(\\s*"+"(?:"+ARGUMENTS+"?"+")"+"\\)\\s*"+")";const BINDING="("+IDENT+"\\s*"+ARGUMENT_LIST+"?"+")";const OPEN_BRACKET="(\\[\\[|{{)"+"\\s*";const CLOSE_BRACKET="(?:]]|}})";const NEGATE="(?:(!)\\s*)?";const EXPRESSION=OPEN_BRACKET+NEGATE+BINDING+CLOSE_BRACKET;const bindingRegex=new RegExp(EXPRESSION,"g");function literalFromParts(parts){let s="";for(let i=0;i<parts.length;i++){let literal=parts[i].literal;s+=literal||""}return s}function parseMethod(expression){let m=expression.match(/([^\s]+?)\(([\s\S]*)\)/);if(m){let methodName=m[1];let sig={methodName:methodName,static:true,args:emptyArray};if(m[2].trim()){let args=m[2].replace(/\\,/g,",").split(",");return parseArgs(args,sig)}else{return sig}}return null}function parseArgs(argList,sig){sig.args=argList.map(function(rawArg){let arg=parseArg(rawArg);if(!arg.literal){sig.static=false}return arg},this);return sig}function parseArg(rawArg){let arg=rawArg.trim().replace(/,/g,",").replace(/\\(.)/g,"$1");let a={name:arg,value:"",literal:false};let fc=arg[0];if(fc==="-"){fc=arg[1]}if(fc>="0"&&fc<="9"){fc="#"}switch(fc){case"'":case'"':a.value=arg.slice(1,-1);a.literal=true;break;case"#":a.value=Number(arg);a.literal=true;break}if(!a.literal){a.rootProperty=root(arg);a.structured=isPath(arg);if(a.structured){a.wildcard=arg.slice(-2)==".*";if(a.wildcard){a.name=arg.slice(0,-2)}}}return a}function getArgValue(data,props,path){let value=get(data,path);if(value===undefined){value=props[path]}return value}function notifySplices(inst,array,path,splices){inst.notifyPath(path+".splices",{indexSplices:splices});inst.notifyPath(path+".length",array.length)}function notifySplice(inst,array,path,index,addedCount,removed){notifySplices(inst,array,path,[{index:index,addedCount:addedCount,removed:removed,object:array,type:"splice"}])}function upper(name){return name[0].toUpperCase()+name.substring(1)}const PropertyEffects=dedupingMixin(superClass=>{const propertyEffectsBase=TemplateStamp(PropertyAccessors(superClass));class PropertyEffects extends propertyEffectsBase{constructor(){super();this.__isPropertyEffectsClient=true;this.__dataCounter=0;this.__dataClientsReady;this.__dataPendingClients;this.__dataToNotify;this.__dataLinkedPaths;this.__dataHasPaths;this.__dataCompoundStorage;this.__dataHost;this.__dataTemp;this.__dataClientsInitialized;this.__data;this.__dataPending;this.__dataOld;this.__computeEffects;this.__reflectEffects;this.__notifyEffects;this.__propagateEffects;this.__observeEffects;this.__readOnly;this.__templateInfo}get PROPERTY_EFFECT_TYPES(){return TYPES}_initializeProperties(){super._initializeProperties();hostStack.registerHost(this);this.__dataClientsReady=false;this.__dataPendingClients=null;this.__dataToNotify=null;this.__dataLinkedPaths=null;this.__dataHasPaths=false;this.__dataCompoundStorage=this.__dataCompoundStorage||null;this.__dataHost=this.__dataHost||null;this.__dataTemp={};this.__dataClientsInitialized=false}_initializeProtoProperties(props){this.__data=Object.create(props);this.__dataPending=Object.create(props);this.__dataOld={}}_initializeInstanceProperties(props){let readOnly=this[TYPES.READ_ONLY];for(let prop in props){if(!readOnly||!readOnly[prop]){this.__dataPending=this.__dataPending||{};this.__dataOld=this.__dataOld||{};this.__data[prop]=this.__dataPending[prop]=props[prop]}}}_addPropertyEffect(property,type,effect){this._createPropertyAccessor(property,type==TYPES.READ_ONLY);let effects=ensureOwnEffectMap(this,type)[property];if(!effects){effects=this[type][property]=[]}effects.push(effect)}_removePropertyEffect(property,type,effect){let effects=ensureOwnEffectMap(this,type)[property];let idx=effects.indexOf(effect);if(idx>=0){effects.splice(idx,1)}}_hasPropertyEffect(property,type){let effects=this[type];return Boolean(effects&&effects[property])}_hasReadOnlyEffect(property){return this._hasPropertyEffect(property,TYPES.READ_ONLY)}_hasNotifyEffect(property){return this._hasPropertyEffect(property,TYPES.NOTIFY)}_hasReflectEffect(property){return this._hasPropertyEffect(property,TYPES.REFLECT)}_hasComputedEffect(property){return this._hasPropertyEffect(property,TYPES.COMPUTE)}_setPendingPropertyOrPath(path,value,shouldNotify,isPathNotification){if(isPathNotification||root(Array.isArray(path)?path[0]:path)!==path){if(!isPathNotification){let old=get(this,path);path=set(this,path,value);if(!path||!super._shouldPropertyChange(path,value,old)){return false}}this.__dataHasPaths=true;if(this._setPendingProperty(path,value,shouldNotify)){computeLinkedPaths(this,path,value);return true}}else{if(this.__dataHasAccessor&&this.__dataHasAccessor[path]){return this._setPendingProperty(path,value,shouldNotify)}else{this[path]=value}}return false}_setUnmanagedPropertyToNode(node,prop,value){if(value!==node[prop]||typeof value=="object"){node[prop]=value}}_setPendingProperty(property,value,shouldNotify){let propIsPath=this.__dataHasPaths&&isPath(property);let prevProps=propIsPath?this.__dataTemp:this.__data;if(this._shouldPropertyChange(property,value,prevProps[property])){if(!this.__dataPending){this.__dataPending={};this.__dataOld={}}if(!(property in this.__dataOld)){this.__dataOld[property]=this.__data[property]}if(propIsPath){this.__dataTemp[property]=value}else{this.__data[property]=value}this.__dataPending[property]=value;if(propIsPath||this[TYPES.NOTIFY]&&this[TYPES.NOTIFY][property]){this.__dataToNotify=this.__dataToNotify||{};this.__dataToNotify[property]=shouldNotify}return true}return false}_setProperty(property,value){if(this._setPendingProperty(property,value,true)){this._invalidateProperties()}}_invalidateProperties(){if(this.__dataReady){this._flushProperties()}}_enqueueClient(client){this.__dataPendingClients=this.__dataPendingClients||[];if(client!==this){this.__dataPendingClients.push(client)}}_flushProperties(){this.__dataCounter++;super._flushProperties();this.__dataCounter--}_flushClients(){if(!this.__dataClientsReady){this.__dataClientsReady=true;this._readyClients();this.__dataReady=true}else{this.__enableOrFlushClients()}}__enableOrFlushClients(){let clients=this.__dataPendingClients;if(clients){this.__dataPendingClients=null;for(let i=0;i<clients.length;i++){let client=clients[i];if(!client.__dataEnabled){client._enableProperties()}else if(client.__dataPending){client._flushProperties()}}}}_readyClients(){this.__enableOrFlushClients()}setProperties(props,setReadOnly){for(let path in props){if(setReadOnly||!this[TYPES.READ_ONLY]||!this[TYPES.READ_ONLY][path]){this._setPendingPropertyOrPath(path,props[path],true)}}this._invalidateProperties()}ready(){this._flushProperties();if(!this.__dataClientsReady){this._flushClients()}if(this.__dataPending){this._flushProperties()}}_propertiesChanged(currentProps,changedProps,oldProps){let hasPaths=this.__dataHasPaths;this.__dataHasPaths=false;runComputedEffects(this,changedProps,oldProps,hasPaths);let notifyProps=this.__dataToNotify;this.__dataToNotify=null;this._propagatePropertyChanges(changedProps,oldProps,hasPaths);this._flushClients();runEffects(this,this[TYPES.REFLECT],changedProps,oldProps,hasPaths);runEffects(this,this[TYPES.OBSERVE],changedProps,oldProps,hasPaths);if(notifyProps){runNotifyEffects(this,notifyProps,changedProps,oldProps,hasPaths)}if(this.__dataCounter==1){this.__dataTemp={}}}_propagatePropertyChanges(changedProps,oldProps,hasPaths){if(this[TYPES.PROPAGATE]){runEffects(this,this[TYPES.PROPAGATE],changedProps,oldProps,hasPaths)}let templateInfo=this.__templateInfo;while(templateInfo){runEffects(this,templateInfo.propertyEffects,changedProps,oldProps,hasPaths,templateInfo.nodeList);templateInfo=templateInfo.nextTemplateInfo}}linkPaths(to,from){to=normalize(to);from=normalize(from);this.__dataLinkedPaths=this.__dataLinkedPaths||{};this.__dataLinkedPaths[to]=from}unlinkPaths(path){path=normalize(path);if(this.__dataLinkedPaths){delete this.__dataLinkedPaths[path]}}notifySplices(path,splices){let info={path:""};let array=get(this,path,info);notifySplices(this,array,info.path,splices)}get(path,root){return get(root||this,path)}set(path,value,root){if(root){set(root,path,value)}else{if(!this[TYPES.READ_ONLY]||!this[TYPES.READ_ONLY][path]){if(this._setPendingPropertyOrPath(path,value,true)){this._invalidateProperties()}}}}push(path,...items){let info={path:""};let array=get(this,path,info);let len=array.length;let ret=array.push(...items);if(items.length){notifySplice(this,array,info.path,len,items.length,[])}return ret}pop(path){let info={path:""};let array=get(this,path,info);let hadLength=Boolean(array.length);let ret=array.pop();if(hadLength){notifySplice(this,array,info.path,array.length,0,[ret])}return ret}splice(path,start,deleteCount,...items){let info={path:""};let array=get(this,path,info);if(start<0){start=array.length-Math.floor(-start)}else if(start){start=Math.floor(start)}let ret;if(arguments.length===2){ret=array.splice(start)}else{ret=array.splice(start,deleteCount,...items)}if(items.length||ret.length){notifySplice(this,array,info.path,start,items.length,ret)}return ret}shift(path){let info={path:""};let array=get(this,path,info);let hadLength=Boolean(array.length);let ret=array.shift();if(hadLength){notifySplice(this,array,info.path,0,0,[ret])}return ret}unshift(path,...items){let info={path:""};let array=get(this,path,info);let ret=array.unshift(...items);if(items.length){notifySplice(this,array,info.path,0,items.length,[])}return ret}notifyPath(path,value){let propPath;if(arguments.length==1){let info={path:""};value=get(this,path,info);propPath=info.path}else if(Array.isArray(path)){propPath=normalize(path)}else{propPath=path}if(this._setPendingPropertyOrPath(propPath,value,true,true)){this._invalidateProperties()}}_createReadOnlyProperty(property,protectedSetter){this._addPropertyEffect(property,TYPES.READ_ONLY);if(protectedSetter){this["_set"+upper(property)]=function(value){this._setProperty(property,value)}}}_createPropertyObserver(property,method,dynamicFn){let info={property:property,method:method,dynamicFn:Boolean(dynamicFn)};this._addPropertyEffect(property,TYPES.OBSERVE,{fn:runObserverEffect,info:info,trigger:{name:property}});if(dynamicFn){this._addPropertyEffect(method,TYPES.OBSERVE,{fn:runObserverEffect,info:info,trigger:{name:method}})}}_createMethodObserver(expression,dynamicFn){let sig=parseMethod(expression);if(!sig){throw new Error("Malformed observer expression '"+expression+"'")}createMethodEffect(this,sig,TYPES.OBSERVE,runMethodEffect,null,dynamicFn)}_createNotifyingProperty(property){this._addPropertyEffect(property,TYPES.NOTIFY,{fn:runNotifyEffect,info:{eventName:camelToDashCase(property)+"-changed",property:property}})}_createReflectedProperty(property){let attr=this.constructor.attributeNameForProperty(property);if(attr[0]==="-"){console.warn("Property "+property+" cannot be reflected to attribute "+attr+' because "-" is not a valid starting attribute name. Use a lowercase first letter for the property instead.')}else{this._addPropertyEffect(property,TYPES.REFLECT,{fn:runReflectEffect,info:{attrName:attr}})}}_createComputedProperty(property,expression,dynamicFn){let sig=parseMethod(expression);if(!sig){throw new Error("Malformed computed expression '"+expression+"'")}createMethodEffect(this,sig,TYPES.COMPUTE,runComputedEffect,property,dynamicFn)}_marshalArgs(args,path,props){const data=this.__data;const values=[];for(let i=0,l=args.length;i<l;i++){let{name:name,structured:structured,wildcard:wildcard,value:value,literal:literal}=args[i];if(!literal){if(wildcard){const matches=isDescendant(name,path);const pathValue=getArgValue(data,props,matches?path:name);value={path:matches?path:name,value:pathValue,base:matches?get(data,name):pathValue}}else{value=structured?getArgValue(data,props,name):data[name]}}values[i]=value}return values}static addPropertyEffect(property,type,effect){this.prototype._addPropertyEffect(property,type,effect)}static createPropertyObserver(property,method,dynamicFn){this.prototype._createPropertyObserver(property,method,dynamicFn)}static createMethodObserver(expression,dynamicFn){this.prototype._createMethodObserver(expression,dynamicFn)}static createNotifyingProperty(property){this.prototype._createNotifyingProperty(property)}static createReadOnlyProperty(property,protectedSetter){this.prototype._createReadOnlyProperty(property,protectedSetter)}static createReflectedProperty(property){this.prototype._createReflectedProperty(property)}static createComputedProperty(property,expression,dynamicFn){this.prototype._createComputedProperty(property,expression,dynamicFn)}static bindTemplate(template){return this.prototype._bindTemplate(template)}_bindTemplate(template,instanceBinding){let templateInfo=this.constructor._parseTemplate(template);let wasPreBound=this.__templateInfo==templateInfo;if(!wasPreBound){for(let prop in templateInfo.propertyEffects){this._createPropertyAccessor(prop)}}if(instanceBinding){templateInfo=Object.create(templateInfo);templateInfo.wasPreBound=wasPreBound;if(!wasPreBound&&this.__templateInfo){let last=this.__templateInfoLast||this.__templateInfo;this.__templateInfoLast=last.nextTemplateInfo=templateInfo;templateInfo.previousTemplateInfo=last;return templateInfo}}return this.__templateInfo=templateInfo}static _addTemplatePropertyEffect(templateInfo,prop,effect){let hostProps=templateInfo.hostProps=templateInfo.hostProps||{};hostProps[prop]=true;let effects=templateInfo.propertyEffects=templateInfo.propertyEffects||{};let propEffects=effects[prop]=effects[prop]||[];propEffects.push(effect)}_stampTemplate(template){hostStack.beginHosting(this);let dom=super._stampTemplate(template);hostStack.endHosting(this);let templateInfo=this._bindTemplate(template,true);templateInfo.nodeList=dom.nodeList;if(!templateInfo.wasPreBound){let nodes=templateInfo.childNodes=[];for(let n=dom.firstChild;n;n=n.nextSibling){nodes.push(n)}}dom.templateInfo=templateInfo;setupBindings(this,templateInfo);if(this.__dataReady){runEffects(this,templateInfo.propertyEffects,this.__data,null,false,templateInfo.nodeList)}return dom}_removeBoundDom(dom){let templateInfo=dom.templateInfo;if(templateInfo.previousTemplateInfo){templateInfo.previousTemplateInfo.nextTemplateInfo=templateInfo.nextTemplateInfo}if(templateInfo.nextTemplateInfo){templateInfo.nextTemplateInfo.previousTemplateInfo=templateInfo.previousTemplateInfo}if(this.__templateInfoLast==templateInfo){this.__templateInfoLast=templateInfo.previousTemplateInfo}templateInfo.previousTemplateInfo=templateInfo.nextTemplateInfo=null;let nodes=templateInfo.childNodes;for(let i=0;i<nodes.length;i++){let node=nodes[i];node.parentNode.removeChild(node)}}static _parseTemplateNode(node,templateInfo,nodeInfo){let noted=super._parseTemplateNode(node,templateInfo,nodeInfo);if(node.nodeType===Node.TEXT_NODE){let parts=this._parseBindings(node.textContent,templateInfo);if(parts){node.textContent=literalFromParts(parts)||" ";addBinding(this,templateInfo,nodeInfo,"text","textContent",parts);noted=true}}return noted}static _parseTemplateNodeAttribute(node,templateInfo,nodeInfo,name,value){let parts=this._parseBindings(value,templateInfo);if(parts){let origName=name;let kind="property";if(capitalAttributeRegex.test(name)){kind="attribute"}else if(name[name.length-1]=="$"){name=name.slice(0,-1);kind="attribute"}let literal=literalFromParts(parts);if(literal&&kind=="attribute"){if(name=="class"&&node.hasAttribute("class")){literal+=" "+node.getAttribute(name)}node.setAttribute(name,literal)}if(node.localName==="input"&&origName==="value"){node.setAttribute(origName,"")}node.removeAttribute(origName);if(kind==="property"){name=dashToCamelCase(name)}addBinding(this,templateInfo,nodeInfo,kind,name,parts,literal);return true}else{return super._parseTemplateNodeAttribute(node,templateInfo,nodeInfo,name,value)}}static _parseTemplateNestedTemplate(node,templateInfo,nodeInfo){let noted=super._parseTemplateNestedTemplate(node,templateInfo,nodeInfo);let hostProps=nodeInfo.templateInfo.hostProps;let mode="{";for(let source in hostProps){let parts=[{mode:mode,source:source,dependencies:[source]}];addBinding(this,templateInfo,nodeInfo,"property","_host_"+source,parts)}return noted}static _parseBindings(text,templateInfo){let parts=[];let lastIndex=0;let m;while((m=bindingRegex.exec(text))!==null){if(m.index>lastIndex){parts.push({literal:text.slice(lastIndex,m.index)})}let mode=m[1][0];let negate=Boolean(m[2]);let source=m[3].trim();let customEvent=false,notifyEvent="",colon=-1;if(mode=="{"&&(colon=source.indexOf("::"))>0){notifyEvent=source.substring(colon+2);source=source.substring(0,colon);customEvent=true}let signature=parseMethod(source);let dependencies=[];if(signature){let{args:args,methodName:methodName}=signature;for(let i=0;i<args.length;i++){let arg=args[i];if(!arg.literal){dependencies.push(arg)}}let dynamicFns=templateInfo.dynamicFns;if(dynamicFns&&dynamicFns[methodName]||signature.static){dependencies.push(methodName);signature.dynamicFn=true}}else{dependencies.push(source)}parts.push({source:source,mode:mode,negate:negate,customEvent:customEvent,signature:signature,dependencies:dependencies,event:notifyEvent});lastIndex=bindingRegex.lastIndex}if(lastIndex&&lastIndex<text.length){let literal=text.substring(lastIndex);if(literal){parts.push({literal:literal})}}if(parts.length){return parts}else{return null}}static _evaluateBinding(inst,part,path,props,oldProps,hasPaths){let value;if(part.signature){value=runMethodEffect(inst,path,props,oldProps,part.signature)}else if(path!=part.source){value=get(inst,part.source)}else{if(hasPaths&&isPath(path)){value=get(inst,path)}else{value=inst.__data[path]}}if(part.negate){value=!value}return value}}return PropertyEffects});class HostStack{constructor(){this.stack=[]}registerHost(inst){if(this.stack.length){let host=this.stack[this.stack.length-1];host._enqueueClient(inst)}}beginHosting(inst){this.stack.push(inst)}endHosting(inst){let stackLen=this.stack.length;if(stackLen&&this.stack[stackLen-1]==inst){this.stack.pop()}}}const hostStack=new HostStack;function register$1(prototype){}function normalizeProperties(props){const output={};for(let p in props){const o=props[p];output[p]=typeof o==="function"?{type:o}:o}return output}const PropertiesMixin=dedupingMixin(superClass=>{const base=PropertiesChanged(superClass);function superPropertiesClass(constructor){const superCtor=Object.getPrototypeOf(constructor);return superCtor.prototype instanceof PropertiesMixin?superCtor:null}function ownProperties(constructor){if(!constructor.hasOwnProperty(JSCompiler_renameProperty("__ownProperties",constructor))){let props=null;if(constructor.hasOwnProperty(JSCompiler_renameProperty("properties",constructor))){const properties=constructor.properties;if(properties){props=normalizeProperties(properties)}}constructor.__ownProperties=props}return constructor.__ownProperties}class PropertiesMixin extends base{static get observedAttributes(){if(!this.hasOwnProperty("__observedAttributes")){register$1(this.prototype);const props=this._properties;this.__observedAttributes=props?Object.keys(props).map(p=>this.attributeNameForProperty(p)):[]}return this.__observedAttributes}static finalize(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__finalized",this))){const superCtor=superPropertiesClass(this);if(superCtor){superCtor.finalize()}this.__finalized=true;this._finalizeClass()}}static _finalizeClass(){const props=ownProperties(this);if(props){this.createProperties(props)}}static get _properties(){if(!this.hasOwnProperty(JSCompiler_renameProperty("__properties",this))){const superCtor=superPropertiesClass(this);this.__properties=Object.assign({},superCtor&&superCtor._properties,ownProperties(this))}return this.__properties}static typeForProperty(name){const info=this._properties[name];return info&&info.type}_initializeProperties(){this.constructor.finalize();super._initializeProperties()}connectedCallback(){if(super.connectedCallback){super.connectedCallback()}this._enableProperties()}disconnectedCallback(){if(super.disconnectedCallback){super.disconnectedCallback()}}}return PropertiesMixin});const version="3.2.0";const builtCSS=window.ShadyCSS&&window.ShadyCSS["cssBuild"];const ElementMixin=dedupingMixin(base=>{const polymerElementBase=PropertiesMixin(PropertyEffects(base));function propertyDefaults(constructor){if(!constructor.hasOwnProperty(JSCompiler_renameProperty("__propertyDefaults",constructor))){constructor.__propertyDefaults=null;let props=constructor._properties;for(let p in props){let info=props[p];if("value"in info){constructor.__propertyDefaults=constructor.__propertyDefaults||{};constructor.__propertyDefaults[p]=info}}}return constructor.__propertyDefaults}function ownObservers(constructor){if(!constructor.hasOwnProperty(JSCompiler_renameProperty("__ownObservers",constructor))){constructor.__ownObservers=constructor.hasOwnProperty(JSCompiler_renameProperty("observers",constructor))?constructor.observers:null}return constructor.__ownObservers}function createPropertyFromConfig(proto,name,info,allProps){if(info.computed){info.readOnly=true}if(info.computed){if(proto._hasReadOnlyEffect(name)){console.warn(`Cannot redefine computed property '${name}'.`)}else{proto._createComputedProperty(name,info.computed,allProps)}}if(info.readOnly&&!proto._hasReadOnlyEffect(name)){proto._createReadOnlyProperty(name,!info.computed)}else if(info.readOnly===false&&proto._hasReadOnlyEffect(name)){console.warn(`Cannot make readOnly property '${name}' non-readOnly.`)}if(info.reflectToAttribute&&!proto._hasReflectEffect(name)){proto._createReflectedProperty(name)}else if(info.reflectToAttribute===false&&proto._hasReflectEffect(name)){console.warn(`Cannot make reflected property '${name}' non-reflected.`)}if(info.notify&&!proto._hasNotifyEffect(name)){proto._createNotifyingProperty(name)}else if(info.notify===false&&proto._hasNotifyEffect(name)){console.warn(`Cannot make notify property '${name}' non-notify.`)}if(info.observer){proto._createPropertyObserver(name,info.observer,allProps[info.observer])}proto._addPropertyToAttributeMap(name)}function processElementStyles(klass,template,is,baseURI){if(!builtCSS){const templateStyles=template.content.querySelectorAll("style");const stylesWithImports=stylesFromTemplate(template);const linkedStyles=stylesFromModuleImports(is);const firstTemplateChild=template.content.firstElementChild;for(let idx=0;idx<linkedStyles.length;idx++){let s=linkedStyles[idx];s.textContent=klass._processStyleText(s.textContent,baseURI);template.content.insertBefore(s,firstTemplateChild)}let templateStyleIndex=0;for(let i=0;i<stylesWithImports.length;i++){let s=stylesWithImports[i];let templateStyle=templateStyles[templateStyleIndex];if(templateStyle!==s){s=s.cloneNode(true);templateStyle.parentNode.insertBefore(s,templateStyle)}else{templateStyleIndex++}s.textContent=klass._processStyleText(s.textContent,baseURI)}}if(window.ShadyCSS){window.ShadyCSS.prepareTemplate(template,is)}}function getTemplateFromDomModule(is){let template=null;if(is&&(!strictTemplatePolicy||allowTemplateFromDomModule)){template=DomModule.import(is,"template");if(strictTemplatePolicy&&!template){throw new Error(`strictTemplatePolicy: expecting dom-module or null template for ${is}`)}}return template}class PolymerElement extends polymerElementBase{static get polymerElementVersion(){return version}static _finalizeClass(){super._finalizeClass();const observers=ownObservers(this);if(observers){this.createObservers(observers,this._properties)}this._prepareTemplate()}static _prepareTemplate(){let template=this.template;if(template){if(typeof template==="string"){console.error("template getter must return HTMLTemplateElement");template=null}else if(!legacyOptimizations){template=template.cloneNode(true)}}this.prototype._template=template}static createProperties(props){for(let p in props){createPropertyFromConfig(this.prototype,p,props[p],props)}}static createObservers(observers,dynamicFns){const proto=this.prototype;for(let i=0;i<observers.length;i++){proto._createMethodObserver(observers[i],dynamicFns)}}static get template(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_template",this))){this._template=this.prototype.hasOwnProperty(JSCompiler_renameProperty("_template",this.prototype))?this.prototype._template:getTemplateFromDomModule(this.is)||Object.getPrototypeOf(this.prototype).constructor.template}return this._template}static set template(value){this._template=value}static get importPath(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_importPath",this))){const meta=this.importMeta;if(meta){this._importPath=pathFromUrl(meta.url)}else{const module=DomModule.import(this.is);this._importPath=module&&module.assetpath||Object.getPrototypeOf(this.prototype).constructor.importPath}}return this._importPath}constructor(){super();this._template;this._importPath;this.rootPath;this.importPath;this.root;this.$}_initializeProperties(){this.constructor.finalize();this.constructor._finalizeTemplate(this.localName);super._initializeProperties();this.rootPath=rootPath;this.importPath=this.constructor.importPath;let p$=propertyDefaults(this.constructor);if(!p$){return}for(let p in p$){let info=p$[p];if(!this.hasOwnProperty(p)){let value=typeof info.value=="function"?info.value.call(this):info.value;if(this._hasAccessor(p)){this._setPendingProperty(p,value,true)}else{this[p]=value}}}}static _processStyleText(cssText,baseURI){return resolveCss(cssText,baseURI)}static _finalizeTemplate(is){const template=this.prototype._template;if(template&&!template.__polymerFinalized){template.__polymerFinalized=true;const importPath=this.importPath;const baseURI=importPath?resolveUrl(importPath):"";processElementStyles(this,template,is,baseURI);this.prototype._bindTemplate(template)}}connectedCallback(){if(window.ShadyCSS&&this._template){window.ShadyCSS.styleElement(this)}super.connectedCallback()}ready(){if(this._template){this.root=this._stampTemplate(this._template);this.$=this.root.$}super.ready()}_readyClients(){if(this._template){this.root=this._attachDom(this.root)}super._readyClients()}_attachDom(dom){const n=wrap(this);if(n.attachShadow){if(dom){if(!n.shadowRoot){n.attachShadow({mode:"open"})}n.shadowRoot.appendChild(dom);if(syncInitialRender&&window.ShadyDOM){ShadyDOM.flushInitial(n.shadowRoot)}return n.shadowRoot}return null}else{throw new Error("ShadowDOM not available. "+"PolymerElement can create dom as children instead of in "+"ShadowDOM by setting `this.root = this;` before `ready`.")}}updateStyles(properties){if(window.ShadyCSS){window.ShadyCSS.styleSubtree(this,properties)}}resolveUrl(url,base){if(!base&&this.importPath){base=resolveUrl(this.importPath)}return resolveUrl(url,base)}static _parseTemplateContent(template,templateInfo,nodeInfo){templateInfo.dynamicFns=templateInfo.dynamicFns||this._properties;return super._parseTemplateContent(template,templateInfo,nodeInfo)}static _addTemplatePropertyEffect(templateInfo,prop,effect){if(legacyOptimizations&&!(prop in this._properties)){console.warn(`Property '${prop}' used in template but not declared in 'properties'; `+`attribute will not be observed.`)}return super._addTemplatePropertyEffect(templateInfo,prop,effect)}}return PolymerElement});const GestureEventListeners=dedupingMixin(superClass=>{class GestureEventListeners extends superClass{_addEventListenerToNode(node,eventName,handler){if(!addListener(node,eventName,handler)){super._addEventListenerToNode(node,eventName,handler)}}_removeEventListenerFromNode(node,eventName,handler){if(!removeListener(node,eventName,handler)){super._removeEventListenerFromNode(node,eventName,handler)}}}return GestureEventListeners});function resolve(){document.body.removeAttribute("unresolved")}if(document.readyState==="interactive"||document.readyState==="complete"){resolve()}else{window.addEventListener("DOMContentLoaded",resolve)}function newSplice(index,removed,addedCount){return{index:index,removed:removed,addedCount:addedCount}}const EDIT_LEAVE=0;const EDIT_UPDATE=1;const EDIT_ADD=2;const EDIT_DELETE=3;function calcEditDistances(current,currentStart,currentEnd,old,oldStart,oldEnd){let rowCount=oldEnd-oldStart+1;let columnCount=currentEnd-currentStart+1;let distances=new Array(rowCount);for(let i=0;i<rowCount;i++){distances[i]=new Array(columnCount);distances[i][0]=i}for(let j=0;j<columnCount;j++)distances[0][j]=j;for(let i=1;i<rowCount;i++){for(let j=1;j<columnCount;j++){if(equals(current[currentStart+j-1],old[oldStart+i-1]))distances[i][j]=distances[i-1][j-1];else{let north=distances[i-1][j]+1;let west=distances[i][j-1]+1;distances[i][j]=north<west?north:west}}}return distances}function spliceOperationsFromEditDistances(distances){let i=distances.length-1;let j=distances[0].length-1;let current=distances[i][j];let edits=[];while(i>0||j>0){if(i==0){edits.push(EDIT_ADD);j--;continue}if(j==0){edits.push(EDIT_DELETE);i--;continue}let northWest=distances[i-1][j-1];let west=distances[i-1][j];let north=distances[i][j-1];let min;if(west<north)min=west<northWest?west:northWest;else min=north<northWest?north:northWest;if(min==northWest){if(northWest==current){edits.push(EDIT_LEAVE)}else{edits.push(EDIT_UPDATE);current=northWest}i--;j--}else if(min==west){edits.push(EDIT_DELETE);i--;current=west}else{edits.push(EDIT_ADD);j--;current=north}}edits.reverse();return edits}function calcSplices(current,currentStart,currentEnd,old,oldStart,oldEnd){let prefixCount=0;let suffixCount=0;let splice;let minLength=Math.min(currentEnd-currentStart,oldEnd-oldStart);if(currentStart==0&&oldStart==0)prefixCount=sharedPrefix(current,old,minLength);if(currentEnd==current.length&&oldEnd==old.length)suffixCount=sharedSuffix(current,old,minLength-prefixCount);currentStart+=prefixCount;oldStart+=prefixCount;currentEnd-=suffixCount;oldEnd-=suffixCount;if(currentEnd-currentStart==0&&oldEnd-oldStart==0)return[];if(currentStart==currentEnd){splice=newSplice(currentStart,[],0);while(oldStart<oldEnd)splice.removed.push(old[oldStart++]);return[splice]}else if(oldStart==oldEnd)return[newSplice(currentStart,[],currentEnd-currentStart)];let ops=spliceOperationsFromEditDistances(calcEditDistances(current,currentStart,currentEnd,old,oldStart,oldEnd));splice=undefined;let splices=[];let index=currentStart;let oldIndex=oldStart;for(let i=0;i<ops.length;i++){switch(ops[i]){case EDIT_LEAVE:if(splice){splices.push(splice);splice=undefined}index++;oldIndex++;break;case EDIT_UPDATE:if(!splice)splice=newSplice(index,[],0);splice.addedCount++;index++;splice.removed.push(old[oldIndex]);oldIndex++;break;case EDIT_ADD:if(!splice)splice=newSplice(index,[],0);splice.addedCount++;index++;break;case EDIT_DELETE:if(!splice)splice=newSplice(index,[],0);splice.removed.push(old[oldIndex]);oldIndex++;break}}if(splice){splices.push(splice)}return splices}function sharedPrefix(current,old,searchLength){for(let i=0;i<searchLength;i++)if(!equals(current[i],old[i]))return i;return searchLength}function sharedSuffix(current,old,searchLength){let index1=current.length;let index2=old.length;let count=0;while(count<searchLength&&equals(current[--index1],old[--index2]))count++;return count}function calculateSplices(current,previous){return calcSplices(current,0,current.length,previous,0,previous.length)}function equals(currentValue,previousValue){return currentValue===previousValue}function isSlot(node){return node.localName==="slot"}let FlattenedNodesObserver=class{static getFlattenedNodes(node){const wrapped=wrap(node);if(isSlot(node)){node=node;return wrapped.assignedNodes({flatten:true})}else{return Array.from(wrapped.childNodes).map(node=>{if(isSlot(node)){node=node;return wrap(node).assignedNodes({flatten:true})}else{return[node]}}).reduce((a,b)=>a.concat(b),[])}}constructor(target,callback){this._shadyChildrenObserver=null;this._nativeChildrenObserver=null;this._connected=false;this._target=target;this.callback=callback;this._effectiveNodes=[];this._observer=null;this._scheduled=false;this._boundSchedule=(()=>{this._schedule()});this.connect();this._schedule()}connect(){if(isSlot(this._target)){this._listenSlots([this._target])}else if(wrap(this._target).children){this._listenSlots(wrap(this._target).children);if(window.ShadyDOM){this._shadyChildrenObserver=ShadyDOM.observeChildren(this._target,mutations=>{this._processMutations(mutations)})}else{this._nativeChildrenObserver=new MutationObserver(mutations=>{this._processMutations(mutations)});this._nativeChildrenObserver.observe(this._target,{childList:true})}}this._connected=true}disconnect(){if(isSlot(this._target)){this._unlistenSlots([this._target])}else if(wrap(this._target).children){this._unlistenSlots(wrap(this._target).children);if(window.ShadyDOM&&this._shadyChildrenObserver){ShadyDOM.unobserveChildren(this._shadyChildrenObserver);this._shadyChildrenObserver=null}else if(this._nativeChildrenObserver){this._nativeChildrenObserver.disconnect();this._nativeChildrenObserver=null}}this._connected=false}_schedule(){if(!this._scheduled){this._scheduled=true;microTask.run(()=>this.flush())}}_processMutations(mutations){this._processSlotMutations(mutations);this.flush()}_processSlotMutations(mutations){if(mutations){for(let i=0;i<mutations.length;i++){let mutation=mutations[i];if(mutation.addedNodes){this._listenSlots(mutation.addedNodes)}if(mutation.removedNodes){this._unlistenSlots(mutation.removedNodes)}}}}flush(){if(!this._connected){return false}if(window.ShadyDOM){ShadyDOM.flush()}if(this._nativeChildrenObserver){this._processSlotMutations(this._nativeChildrenObserver.takeRecords())}else if(this._shadyChildrenObserver){this._processSlotMutations(this._shadyChildrenObserver.takeRecords())}this._scheduled=false;let info={target:this._target,addedNodes:[],removedNodes:[]};let newNodes=this.constructor.getFlattenedNodes(this._target);let splices=calculateSplices(newNodes,this._effectiveNodes);for(let i=0,s;i<splices.length&&(s=splices[i]);i++){for(let j=0,n;j<s.removed.length&&(n=s.removed[j]);j++){info.removedNodes.push(n)}}for(let i=0,s;i<splices.length&&(s=splices[i]);i++){for(let j=s.index;j<s.index+s.addedCount;j++){info.addedNodes.push(newNodes[j])}}this._effectiveNodes=newNodes;let didFlush=false;if(info.addedNodes.length||info.removedNodes.length){didFlush=true;this.callback.call(this._target,info)}return didFlush}_listenSlots(nodeList){for(let i=0;i<nodeList.length;i++){let n=nodeList[i];if(isSlot(n)){n.addEventListener("slotchange",this._boundSchedule)}}}_unlistenSlots(nodeList){for(let i=0;i<nodeList.length;i++){let n=nodeList[i];if(isSlot(n)){n.removeEventListener("slotchange",this._boundSchedule)}}}};const flush=function(){let shadyDOM,debouncers;do{shadyDOM=window.ShadyDOM&&ShadyDOM.flush();if(window.ShadyCSS&&window.ShadyCSS.ScopingShim){window.ShadyCSS.ScopingShim.flush()}debouncers=flushDebouncers()}while(shadyDOM||debouncers)};const p=Element.prototype;const normalizedMatchesSelector=p.matches||p.matchesSelector||p.mozMatchesSelector||p.msMatchesSelector||p.oMatchesSelector||p.webkitMatchesSelector;const matchesSelector=function(node,selector){return normalizedMatchesSelector.call(node,selector)};class DomApiNative{constructor(node){this.node=node}observeNodes(callback){return new FlattenedNodesObserver(this.node,callback)}unobserveNodes(observerHandle){observerHandle.disconnect()}notifyObserver(){}deepContains(node){if(wrap(this.node).contains(node)){return true}let n=node;let doc=node.ownerDocument;while(n&&n!==doc&&n!==this.node){n=wrap(n).parentNode||wrap(n).host}return n===this.node}getOwnerRoot(){return wrap(this.node).getRootNode()}getDistributedNodes(){return this.node.localName==="slot"?wrap(this.node).assignedNodes({flatten:true}):[]}getDestinationInsertionPoints(){let ip$=[];let n=wrap(this.node).assignedSlot;while(n){ip$.push(n);n=wrap(n).assignedSlot}return ip$}importNode(node,deep){let doc=this.node instanceof Document?this.node:this.node.ownerDocument;return wrap(doc).importNode(node,deep)}getEffectiveChildNodes(){return FlattenedNodesObserver.getFlattenedNodes(this.node)}queryDistributedElements(selector){let c$=this.getEffectiveChildNodes();let list=[];for(let i=0,l=c$.length,c;i<l&&(c=c$[i]);i++){if(c.nodeType===Node.ELEMENT_NODE&&matchesSelector(c,selector)){list.push(c)}}return list}get activeElement(){let node=this.node;return node._activeElement!==undefined?node._activeElement:node.activeElement}}function forwardMethods(proto,methods){for(let i=0;i<methods.length;i++){let method=methods[i];proto[method]=function(){return this.node[method].apply(this.node,arguments)}}}function forwardReadOnlyProperties(proto,properties){for(let i=0;i<properties.length;i++){let name=properties[i];Object.defineProperty(proto,name,{get:function(){const domApi=this;return domApi.node[name]},configurable:true})}}function forwardProperties(proto,properties){for(let i=0;i<properties.length;i++){let name=properties[i];Object.defineProperty(proto,name,{get:function(){return this.node[name]},set:function(value){this.node[name]=value},configurable:true})}}class EventApi{constructor(event){this.event=event}get rootTarget(){return this.path[0]}get localTarget(){return this.event.target}get path(){return this.event.composedPath()}}DomApiNative.prototype.cloneNode;DomApiNative.prototype.appendChild;DomApiNative.prototype.insertBefore;DomApiNative.prototype.removeChild;DomApiNative.prototype.replaceChild;DomApiNative.prototype.setAttribute;DomApiNative.prototype.removeAttribute;DomApiNative.prototype.querySelector;DomApiNative.prototype.querySelectorAll;DomApiNative.prototype.parentNode;DomApiNative.prototype.firstChild;DomApiNative.prototype.lastChild;DomApiNative.prototype.nextSibling;DomApiNative.prototype.previousSibling;DomApiNative.prototype.firstElementChild;DomApiNative.prototype.lastElementChild;DomApiNative.prototype.nextElementSibling;DomApiNative.prototype.previousElementSibling;DomApiNative.prototype.childNodes;DomApiNative.prototype.children;DomApiNative.prototype.classList;DomApiNative.prototype.textContent;DomApiNative.prototype.innerHTML;let DomApiImpl=DomApiNative;if(window["ShadyDOM"]&&window["ShadyDOM"]["inUse"]&&window["ShadyDOM"]["noPatch"]&&window["ShadyDOM"]["Wrapper"]){class Wrapper extends window["ShadyDOM"]["Wrapper"]{}Object.getOwnPropertyNames(DomApiNative.prototype).forEach(prop=>{if(prop!="activeElement"){Wrapper.prototype[prop]=DomApiNative.prototype[prop]}});forwardReadOnlyProperties(Wrapper.prototype,["classList"]);DomApiImpl=Wrapper;Object.defineProperties(EventApi.prototype,{localTarget:{get(){return this.event.currentTarget},configurable:true},path:{get(){return window["ShadyDOM"]["composedPath"](this.event)},configurable:true}})}else{forwardMethods(DomApiNative.prototype,["cloneNode","appendChild","insertBefore","removeChild","replaceChild","setAttribute","removeAttribute","querySelector","querySelectorAll"]);forwardReadOnlyProperties(DomApiNative.prototype,["parentNode","firstChild","lastChild","nextSibling","previousSibling","firstElementChild","lastElementChild","nextElementSibling","previousElementSibling","childNodes","children","classList"]);forwardProperties(DomApiNative.prototype,["textContent","innerHTML"])}const dom=function(obj){obj=obj||document;if(obj instanceof DomApiImpl){return obj}if(obj instanceof EventApi){return obj}let helper=obj["__domApi"];if(!helper){if(obj instanceof Event){helper=new EventApi(obj)}else{helper=new DomApiImpl(obj)}obj["__domApi"]=helper}return helper};let styleInterface=window.ShadyCSS;const LegacyElementMixin=dedupingMixin(base=>{const legacyElementBase=GestureEventListeners(ElementMixin(base));const DIRECTION_MAP={x:"pan-x",y:"pan-y",none:"none",all:"auto"};class LegacyElement extends legacyElementBase{constructor(){super();this.isAttached;this.__boundListeners;this._debouncers}static get importMeta(){return this.prototype.importMeta}created(){}connectedCallback(){super.connectedCallback();this.isAttached=true;this.attached()}attached(){}disconnectedCallback(){super.disconnectedCallback();this.isAttached=false;this.detached()}detached(){}attributeChangedCallback(name,old,value,namespace){if(old!==value){super.attributeChangedCallback(name,old,value,namespace);this.attributeChanged(name,old,value)}}attributeChanged(name,old,value){}_initializeProperties(){let proto=Object.getPrototypeOf(this);if(!proto.hasOwnProperty("__hasRegisterFinished")){this._registered();proto.__hasRegisterFinished=true}super._initializeProperties();this.root=this;this.created();this._applyListeners()}_registered(){}ready(){this._ensureAttributes();super.ready()}_ensureAttributes(){}_applyListeners(){}serialize(value){return this._serializeValue(value)}deserialize(value,type){return this._deserializeValue(value,type)}reflectPropertyToAttribute(property,attribute,value){this._propertyToAttribute(property,attribute,value)}serializeValueToAttribute(value,attribute,node){this._valueToNodeAttribute(node||this,value,attribute)}extend(prototype,api){if(!(prototype&&api)){return prototype||api}let n$=Object.getOwnPropertyNames(api);for(let i=0,n;i<n$.length&&(n=n$[i]);i++){let pd=Object.getOwnPropertyDescriptor(api,n);if(pd){Object.defineProperty(prototype,n,pd)}}return prototype}mixin(target,source){for(let i in source){target[i]=source[i]}return target}chainObject(object,prototype){if(object&&prototype&&object!==prototype){object.__proto__=prototype}return object}instanceTemplate(template){let content=this.constructor._contentForTemplate(template);let dom=document.importNode(content,true);return dom}fire(type,detail,options){options=options||{};detail=detail===null||detail===undefined?{}:detail;let event=new Event(type,{bubbles:options.bubbles===undefined?true:options.bubbles,cancelable:Boolean(options.cancelable),composed:options.composed===undefined?true:options.composed});event.detail=detail;let node=options.node||this;wrap(node).dispatchEvent(event);return event}listen(node,eventName,methodName){node=node||this;let hbl=this.__boundListeners||(this.__boundListeners=new WeakMap);let bl=hbl.get(node);if(!bl){bl={};hbl.set(node,bl)}let key=eventName+methodName;if(!bl[key]){bl[key]=this._addMethodEventListenerToNode(node,eventName,methodName,this)}}unlisten(node,eventName,methodName){node=node||this;let bl=this.__boundListeners&&this.__boundListeners.get(node);let key=eventName+methodName;let handler=bl&&bl[key];if(handler){this._removeEventListenerFromNode(node,eventName,handler);bl[key]=null}}setScrollDirection(direction,node){setTouchAction(node||this,DIRECTION_MAP[direction]||"auto")}$$(slctr){return this.root.querySelector(slctr)}get domHost(){let root=wrap(this).getRootNode();return root instanceof DocumentFragment?root.host:root}distributeContent(){const thisEl=this;const domApi=dom(thisEl);if(window.ShadyDOM&&domApi.shadowRoot){ShadyDOM.flush()}}getEffectiveChildNodes(){const thisEl=this;const domApi=dom(thisEl);return domApi.getEffectiveChildNodes()}queryDistributedElements(selector){const thisEl=this;const domApi=dom(thisEl);return domApi.queryDistributedElements(selector)}getEffectiveChildren(){let list=this.getEffectiveChildNodes();return list.filter(function(n){return n.nodeType===Node.ELEMENT_NODE})}getEffectiveTextContent(){let cn=this.getEffectiveChildNodes();let tc=[];for(let i=0,c;c=cn[i];i++){if(c.nodeType!==Node.COMMENT_NODE){tc.push(c.textContent)}}return tc.join("")}queryEffectiveChildren(selector){let e$=this.queryDistributedElements(selector);return e$&&e$[0]}queryAllEffectiveChildren(selector){return this.queryDistributedElements(selector)}getContentChildNodes(slctr){let content=this.root.querySelector(slctr||"slot");return content?dom(content).getDistributedNodes():[]}getContentChildren(slctr){let children=this.getContentChildNodes(slctr).filter(function(n){return n.nodeType===Node.ELEMENT_NODE});return children}isLightDescendant(node){const thisNode=this;return thisNode!==node&&wrap(thisNode).contains(node)&&wrap(thisNode).getRootNode()===wrap(node).getRootNode()}isLocalDescendant(node){return this.root===wrap(node).getRootNode()}scopeSubtree(container,shouldObserve){}getComputedStyleValue(property){return styleInterface.getComputedStyleValue(this,property)}debounce(jobName,callback,wait){this._debouncers=this._debouncers||{};return this._debouncers[jobName]=Debouncer.debounce(this._debouncers[jobName],wait>0?timeOut.after(wait):microTask,callback.bind(this))}isDebouncerActive(jobName){this._debouncers=this._debouncers||{};let debouncer=this._debouncers[jobName];return!!(debouncer&&debouncer.isActive())}flushDebouncer(jobName){this._debouncers=this._debouncers||{};let debouncer=this._debouncers[jobName];if(debouncer){debouncer.flush()}}cancelDebouncer(jobName){this._debouncers=this._debouncers||{};let debouncer=this._debouncers[jobName];if(debouncer){debouncer.cancel()}}async(callback,waitTime){return waitTime>0?timeOut.run(callback.bind(this),waitTime):~microTask.run(callback.bind(this))}cancelAsync(handle){handle<0?microTask.cancel(~handle):timeOut.cancel(handle)}create(tag,props){let elt=document.createElement(tag);if(props){if(elt.setProperties){elt.setProperties(props)}else{for(let n in props){elt[n]=props[n]}}}return elt}elementMatches(selector,node){return matchesSelector(node||this,selector)}toggleAttribute(name,bool){let node=this;if(arguments.length===3){node=arguments[2]}if(arguments.length==1){bool=!node.hasAttribute(name)}if(bool){wrap(node).setAttribute(name,"");return true}else{wrap(node).removeAttribute(name);return false}}toggleClass(name,bool,node){node=node||this;if(arguments.length==1){bool=!node.classList.contains(name)}if(bool){node.classList.add(name)}else{node.classList.remove(name)}}transform(transformText,node){node=node||this;node.style.webkitTransform=transformText;node.style.transform=transformText}translate3d(x,y,z,node){node=node||this;this.transform("translate3d("+x+","+y+","+z+")",node)}arrayDelete(arrayOrPath,item){let index;if(Array.isArray(arrayOrPath)){index=arrayOrPath.indexOf(item);if(index>=0){return arrayOrPath.splice(index,1)}}else{let arr=get(this,arrayOrPath);index=arr.indexOf(item);if(index>=0){return this.splice(arrayOrPath,index,1)}}return null}_logger(level,args){if(Array.isArray(args)&&args.length===1&&Array.isArray(args[0])){args=args[0]}switch(level){case"log":case"warn":case"error":console[level](...args)}}_log(...args){this._logger("log",args)}_warn(...args){this._logger("warn",args)}_error(...args){this._logger("error",args)}_logf(methodName,...args){return["[%s::%s]",this.is,methodName,...args]}}LegacyElement.prototype.is="";return LegacyElement});const lifecycleProps={attached:true,detached:true,ready:true,created:true,beforeRegister:true,registered:true,attributeChanged:true,listeners:true,hostAttributes:true};const excludeOnInfo={attached:true,detached:true,ready:true,created:true,beforeRegister:true,registered:true,attributeChanged:true,behaviors:true,_noAccessors:true};const excludeOnBehaviors=Object.assign({listeners:true,hostAttributes:true,properties:true,observers:true},excludeOnInfo);function copyProperties(source,target,excludeProps){const noAccessors=source._noAccessors;const propertyNames=Object.getOwnPropertyNames(source);for(let i=0;i<propertyNames.length;i++){let p=propertyNames[i];if(p in excludeProps){continue}if(noAccessors){target[p]=source[p]}else{let pd=Object.getOwnPropertyDescriptor(source,p);if(pd){pd.configurable=true;Object.defineProperty(target,p,pd)}}}}function applyBehaviors(proto,behaviors,lifecycle){for(let i=0;i<behaviors.length;i++){applyInfo(proto,behaviors[i],lifecycle,excludeOnBehaviors)}}function applyInfo(proto,info,lifecycle,excludeProps){copyProperties(info,proto,excludeProps);for(let p in lifecycleProps){if(info[p]){lifecycle[p]=lifecycle[p]||[];lifecycle[p].push(info[p])}}}function flattenBehaviors(behaviors,list,exclude){list=list||[];for(let i=behaviors.length-1;i>=0;i--){let b=behaviors[i];if(b){if(Array.isArray(b)){flattenBehaviors(b,list)}else{if(list.indexOf(b)<0&&(!exclude||exclude.indexOf(b)<0)){list.unshift(b)}}}else{console.warn("behavior is null, check for missing or 404 import")}}return list}function mergeProperties(target,source){for(const p in source){const targetInfo=target[p];const sourceInfo=source[p];if(!("value"in sourceInfo)&&targetInfo&&"value"in targetInfo){target[p]=Object.assign({value:targetInfo.value},sourceInfo)}else{target[p]=sourceInfo}}}function GenerateClassFromInfo(info,Base,behaviors){let behaviorList;const lifecycle={};class PolymerGenerated extends Base{static _finalizeClass(){if(!this.hasOwnProperty(JSCompiler_renameProperty("generatedFrom",this))){super._finalizeClass()}else{if(behaviorList){for(let i=0,b;i<behaviorList.length;i++){b=behaviorList[i];if(b.properties){this.createProperties(b.properties)}if(b.observers){this.createObservers(b.observers,b.properties)}}}if(info.properties){this.createProperties(info.properties)}if(info.observers){this.createObservers(info.observers,info.properties)}this._prepareTemplate()}}static get properties(){const properties={};if(behaviorList){for(let i=0;i<behaviorList.length;i++){mergeProperties(properties,behaviorList[i].properties)}}mergeProperties(properties,info.properties);return properties}static get observers(){let observers=[];if(behaviorList){for(let i=0,b;i<behaviorList.length;i++){b=behaviorList[i];if(b.observers){observers=observers.concat(b.observers)}}}if(info.observers){observers=observers.concat(info.observers)}return observers}created(){super.created();const list=lifecycle.created;if(list){for(let i=0;i<list.length;i++){list[i].call(this)}}}_registered(){const generatedProto=PolymerGenerated.prototype;if(!generatedProto.hasOwnProperty("__hasRegisterFinished")){generatedProto.__hasRegisterFinished=true;super._registered();if(legacyOptimizations){copyPropertiesToProto(generatedProto)}const proto=Object.getPrototypeOf(this);let list=lifecycle.beforeRegister;if(list){for(let i=0;i<list.length;i++){list[i].call(proto)}}list=lifecycle.registered;if(list){for(let i=0;i<list.length;i++){list[i].call(proto)}}}}_applyListeners(){super._applyListeners();const list=lifecycle.listeners;if(list){for(let i=0;i<list.length;i++){const listeners=list[i];if(listeners){for(let l in listeners){this._addMethodEventListenerToNode(this,l,listeners[l])}}}}}_ensureAttributes(){const list=lifecycle.hostAttributes;if(list){for(let i=list.length-1;i>=0;i--){const hostAttributes=list[i];for(let a in hostAttributes){this._ensureAttribute(a,hostAttributes[a])}}}super._ensureAttributes()}ready(){super.ready();let list=lifecycle.ready;if(list){for(let i=0;i<list.length;i++){list[i].call(this)}}}attached(){super.attached();let list=lifecycle.attached;if(list){for(let i=0;i<list.length;i++){list[i].call(this)}}}detached(){super.detached();let list=lifecycle.detached;if(list){for(let i=0;i<list.length;i++){list[i].call(this)}}}attributeChanged(name,old,value){super.attributeChanged();let list=lifecycle.attributeChanged;if(list){for(let i=0;i<list.length;i++){list[i].call(this,name,old,value)}}}}if(behaviors){if(!Array.isArray(behaviors)){behaviors=[behaviors]}let superBehaviors=Base.prototype.behaviors;behaviorList=flattenBehaviors(behaviors,null,superBehaviors);PolymerGenerated.prototype.behaviors=superBehaviors?superBehaviors.concat(behaviors):behaviorList}const copyPropertiesToProto=proto=>{if(behaviorList){applyBehaviors(proto,behaviorList,lifecycle)}applyInfo(proto,info,lifecycle,excludeOnInfo)};if(!legacyOptimizations){copyPropertiesToProto(PolymerGenerated.prototype)}PolymerGenerated.generatedFrom=info;return PolymerGenerated}const Class=function(info,mixin){if(!info){console.warn("Polymer.Class requires `info` argument")}let klass=mixin?mixin(LegacyElementMixin(HTMLElement)):LegacyElementMixin(HTMLElement);klass=GenerateClassFromInfo(info,klass,info.behaviors);klass.is=klass.prototype.is=info.is;return klass};const Polymer=function(info){let klass;if(typeof info==="function"){klass=info}else{klass=Polymer.Class(info)}customElements.define(klass.is,klass);return klass};Polymer.Class=Class;function mutablePropertyChange(inst,property,value,old,mutableData){let isObject;if(mutableData){isObject=typeof value==="object"&&value!==null;if(isObject){old=inst.__dataTemp[property]}}let shouldChange=old!==value&&(old===old||value===value);if(isObject&&shouldChange){inst.__dataTemp[property]=value}return shouldChange}const MutableData=dedupingMixin(superClass=>{class MutableData extends superClass{_shouldPropertyChange(property,value,old){return mutablePropertyChange(this,property,value,old,true)}}return MutableData});const OptionalMutableData=dedupingMixin(superClass=>{class OptionalMutableData extends superClass{static get properties(){return{mutableData:Boolean}}_shouldPropertyChange(property,value,old){return mutablePropertyChange(this,property,value,old,this.mutableData)}}return OptionalMutableData});MutableData._mutablePropertyChange=mutablePropertyChange;let newInstance=null;function HTMLTemplateElementExtension(){return newInstance}HTMLTemplateElementExtension.prototype=Object.create(HTMLTemplateElement.prototype,{constructor:{value:HTMLTemplateElementExtension,writable:true}});const DataTemplate=PropertyEffects(HTMLTemplateElementExtension);const MutableDataTemplate=MutableData(DataTemplate);function upgradeTemplate(template,constructor){newInstance=template;Object.setPrototypeOf(template,constructor.prototype);new constructor;newInstance=null}const templateInstanceBase=PropertyEffects(class{});class TemplateInstanceBase extends templateInstanceBase{constructor(props){super();this._configureProperties(props);this.root=this._stampTemplate(this.__dataHost);let children=this.children=[];for(let n=this.root.firstChild;n;n=n.nextSibling){children.push(n);n.__templatizeInstance=this}if(this.__templatizeOwner&&this.__templatizeOwner.__hideTemplateChildren__){this._showHideChildren(true)}let options=this.__templatizeOptions;if(props&&options.instanceProps||!options.instanceProps){this._enableProperties()}}_configureProperties(props){let options=this.__templatizeOptions;if(options.forwardHostProp){for(let hprop in this.__hostProps){this._setPendingProperty(hprop,this.__dataHost["_host_"+hprop])}}for(let iprop in props){this._setPendingProperty(iprop,props[iprop])}}forwardHostProp(prop,value){if(this._setPendingPropertyOrPath(prop,value,false,true)){this.__dataHost._enqueueClient(this)}}_addEventListenerToNode(node,eventName,handler){if(this._methodHost&&this.__templatizeOptions.parentModel){this._methodHost._addEventListenerToNode(node,eventName,e=>{e.model=this;handler(e)})}else{let templateHost=this.__dataHost.__dataHost;if(templateHost){templateHost._addEventListenerToNode(node,eventName,handler)}}}_showHideChildren(hide){let c=this.children;for(let i=0;i<c.length;i++){let n=c[i];if(Boolean(hide)!=Boolean(n.__hideTemplateChildren__)){if(n.nodeType===Node.TEXT_NODE){if(hide){n.__polymerTextContent__=n.textContent;n.textContent=""}else{n.textContent=n.__polymerTextContent__}}else if(n.localName==="slot"){if(hide){n.__polymerReplaced__=document.createComment("hidden-slot");wrap(wrap(n).parentNode).replaceChild(n.__polymerReplaced__,n)}else{const replace=n.__polymerReplaced__;if(replace){wrap(wrap(replace).parentNode).replaceChild(n,replace)}}}else if(n.style){if(hide){n.__polymerDisplay__=n.style.display;n.style.display="none"}else{n.style.display=n.__polymerDisplay__}}}n.__hideTemplateChildren__=hide;if(n._showHideChildren){n._showHideChildren(hide)}}}_setUnmanagedPropertyToNode(node,prop,value){if(node.__hideTemplateChildren__&&node.nodeType==Node.TEXT_NODE&&prop=="textContent"){node.__polymerTextContent__=value}else{super._setUnmanagedPropertyToNode(node,prop,value)}}get parentModel(){let model=this.__parentModel;if(!model){let options;model=this;do{model=model.__dataHost.__dataHost}while((options=model.__templatizeOptions)&&!options.parentModel);this.__parentModel=model}return model}dispatchEvent(event){return true}}TemplateInstanceBase.prototype.__dataHost;TemplateInstanceBase.prototype.__templatizeOptions;TemplateInstanceBase.prototype._methodHost;TemplateInstanceBase.prototype.__templatizeOwner;TemplateInstanceBase.prototype.__hostProps;const MutableTemplateInstanceBase=MutableData(TemplateInstanceBase);function findMethodHost(template){let templateHost=template.__dataHost;return templateHost&&templateHost._methodHost||templateHost}function createTemplatizerClass(template,templateInfo,options){let templatizerBase=options.mutableData?MutableTemplateInstanceBase:TemplateInstanceBase;if(templatize.mixin){templatizerBase=templatize.mixin(templatizerBase)}let klass=class extends templatizerBase{};klass.prototype.__templatizeOptions=options;klass.prototype._bindTemplate(template);addNotifyEffects(klass,template,templateInfo,options);return klass}function addPropagateEffects(template,templateInfo,options){let userForwardHostProp=options.forwardHostProp;if(userForwardHostProp){let klass=templateInfo.templatizeTemplateClass;if(!klass){let templatizedBase=options.mutableData?MutableDataTemplate:DataTemplate;klass=templateInfo.templatizeTemplateClass=class TemplatizedTemplate extends templatizedBase{};let hostProps=templateInfo.hostProps;for(let prop in hostProps){klass.prototype._addPropertyEffect("_host_"+prop,klass.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE,{fn:createForwardHostPropEffect(prop,userForwardHostProp)});klass.prototype._createNotifyingProperty("_host_"+prop)}}upgradeTemplate(template,klass);if(template.__dataProto){Object.assign(template.__data,template.__dataProto)}template.__dataTemp={};template.__dataPending=null;template.__dataOld=null;template._enableProperties()}}function createForwardHostPropEffect(hostProp,userForwardHostProp){return function forwardHostProp(template,prop,props){userForwardHostProp.call(template.__templatizeOwner,prop.substring("_host_".length),props[prop])}}function addNotifyEffects(klass,template,templateInfo,options){let hostProps=templateInfo.hostProps||{};for(let iprop in options.instanceProps){delete hostProps[iprop];let userNotifyInstanceProp=options.notifyInstanceProp;if(userNotifyInstanceProp){klass.prototype._addPropertyEffect(iprop,klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,{fn:createNotifyInstancePropEffect(iprop,userNotifyInstanceProp)})}}if(options.forwardHostProp&&template.__dataHost){for(let hprop in hostProps){klass.prototype._addPropertyEffect(hprop,klass.prototype.PROPERTY_EFFECT_TYPES.NOTIFY,{fn:createNotifyHostPropEffect()})}}}function createNotifyInstancePropEffect(instProp,userNotifyInstanceProp){return function notifyInstanceProp(inst,prop,props){userNotifyInstanceProp.call(inst.__templatizeOwner,inst,prop,props[prop])}}function createNotifyHostPropEffect(){return function notifyHostProp(inst,prop,props){inst.__dataHost._setPendingPropertyOrPath("_host_"+prop,props[prop],true,true)}}function templatize(template,owner,options){if(strictTemplatePolicy&&!findMethodHost(template)){throw new Error("strictTemplatePolicy: template owner not trusted")}options=options||{};if(template.__templatizeOwner){throw new Error("A <template> can only be templatized once")}template.__templatizeOwner=owner;const ctor=owner?owner.constructor:TemplateInstanceBase;let templateInfo=ctor._parseTemplate(template);let baseClass=templateInfo.templatizeInstanceClass;if(!baseClass){baseClass=createTemplatizerClass(template,templateInfo,options);templateInfo.templatizeInstanceClass=baseClass}addPropagateEffects(template,templateInfo,options);let klass=class TemplateInstance extends baseClass{};klass.prototype._methodHost=findMethodHost(template);klass.prototype.__dataHost=template;klass.prototype.__templatizeOwner=owner;klass.prototype.__hostProps=templateInfo.hostProps;klass=klass;return klass}function modelForElement(template,node){let model;while(node){if(model=node.__templatizeInstance){if(model.__dataHost!=template){node=model.__dataHost}else{return model}}else{node=wrap(node).parentNode}}return null}const Templatizer={templatize(template,mutableData){this._templatizerTemplate=template;this.ctor=templatize(template,this,{mutableData:Boolean(mutableData),parentModel:this._parentModel,instanceProps:this._instanceProps,forwardHostProp:this._forwardHostPropV2,notifyInstanceProp:this._notifyInstancePropV2})},stamp(model){return new this.ctor(model)},modelForElement(el){return modelForElement(this._templatizerTemplate,el)}};const domBindBase=GestureEventListeners(OptionalMutableData(PropertyEffects(HTMLElement)));class DomBind extends domBindBase{static get observedAttributes(){return["mutable-data"]}constructor(){super();if(strictTemplatePolicy){throw new Error(`strictTemplatePolicy: dom-bind not allowed`)}this.root=null;this.$=null;this.__children=null}attributeChangedCallback(){this.mutableData=true}connectedCallback(){this.style.display="none";this.render()}disconnectedCallback(){this.__removeChildren()}__insertChildren(){wrap(wrap(this).parentNode).insertBefore(this.root,this)}__removeChildren(){if(this.__children){for(let i=0;i<this.__children.length;i++){this.root.appendChild(this.__children[i])}}}render(){let template;if(!this.__children){template=template||this.querySelector("template");if(!template){let observer=new MutationObserver(()=>{template=this.querySelector("template");if(template){observer.disconnect();this.render()}else{throw new Error("dom-bind requires a <template> child")}});observer.observe(this,{childList:true});return}this.root=this._stampTemplate(template);this.$=this.root.$;this.__children=[];for(let n=this.root.firstChild;n;n=n.nextSibling){this.__children[this.__children.length]=n}this._enableProperties()}this.__insertChildren();this.dispatchEvent(new CustomEvent("dom-change",{bubbles:true,composed:true}))}}customElements.define("dom-bind",DomBind);class LiteralString{constructor(string){this.value=string.toString()}toString(){return this.value}}function literalValue(value){if(value instanceof LiteralString){return value.value}else{throw new Error(`non-literal value passed to Polymer's htmlLiteral function: ${value}`)}}function htmlValue(value){if(value instanceof HTMLTemplateElement){return value.innerHTML}else if(value instanceof LiteralString){return literalValue(value)}else{throw new Error(`non-template value passed to Polymer's html function: ${value}`)}}const html=function html(strings,...values){const template=document.createElement("template");template.innerHTML=values.reduce((acc,v,idx)=>acc+htmlValue(v)+strings[idx+1],strings[0]);return template};const PolymerElement=ElementMixin(HTMLElement);const domRepeatBase=OptionalMutableData(PolymerElement);class DomRepeat extends domRepeatBase{static get is(){return"dom-repeat"}static get template(){return null}static get properties(){return{items:{type:Array},as:{type:String,value:"item"},indexAs:{type:String,value:"index"},itemsIndexAs:{type:String,value:"itemsIndex"},sort:{type:Function,observer:"__sortChanged"},filter:{type:Function,observer:"__filterChanged"},observe:{type:String,observer:"__observeChanged"},delay:Number,renderedItemCount:{type:Number,notify:true,readOnly:true},initialCount:{type:Number,observer:"__initializeChunking"},targetFramerate:{type:Number,value:20},_targetFrameTime:{type:Number,computed:"__computeFrameTime(targetFramerate)"}}}static get observers(){return["__itemsChanged(items.*)"]}constructor(){super();this.__instances=[];this.__limit=Infinity;this.__pool=[];this.__renderDebouncer=null;this.__itemsIdxToInstIdx={};this.__chunkCount=null;this.__lastChunkTime=null;this.__sortFn=null;this.__filterFn=null;this.__observePaths=null;this.__ctor=null;this.__isDetached=true;this.template=null}disconnectedCallback(){super.disconnectedCallback();this.__isDetached=true;for(let i=0;i<this.__instances.length;i++){this.__detachInstance(i)}}connectedCallback(){super.connectedCallback();this.style.display="none";if(this.__isDetached){this.__isDetached=false;let wrappedParent=wrap(wrap(this).parentNode);for(let i=0;i<this.__instances.length;i++){this.__attachInstance(i,wrappedParent)}}}__ensureTemplatized(){if(!this.__ctor){let template=this.template=this.querySelector("template");if(!template){let observer=new MutationObserver(()=>{if(this.querySelector("template")){observer.disconnect();this.__render()}else{throw new Error("dom-repeat requires a <template> child")}});observer.observe(this,{childList:true});return false}let instanceProps={};instanceProps[this.as]=true;instanceProps[this.indexAs]=true;instanceProps[this.itemsIndexAs]=true;this.__ctor=templatize(template,this,{mutableData:this.mutableData,parentModel:true,instanceProps:instanceProps,forwardHostProp:function(prop,value){let i$=this.__instances;for(let i=0,inst;i<i$.length&&(inst=i$[i]);i++){inst.forwardHostProp(prop,value)}},notifyInstanceProp:function(inst,prop,value){if(matches(this.as,prop)){let idx=inst[this.itemsIndexAs];if(prop==this.as){this.items[idx]=value}let path=translate(this.as,`${JSCompiler_renameProperty("items",this)}.${idx}`,prop);this.notifyPath(path,value)}}})}return true}__getMethodHost(){return this.__dataHost._methodHost||this.__dataHost}__functionFromPropertyValue(functionOrMethodName){if(typeof functionOrMethodName==="string"){let methodName=functionOrMethodName;let obj=this.__getMethodHost();return function(){return obj[methodName].apply(obj,arguments)}}return functionOrMethodName}__sortChanged(sort){this.__sortFn=this.__functionFromPropertyValue(sort);if(this.items){this.__debounceRender(this.__render)}}__filterChanged(filter){this.__filterFn=this.__functionFromPropertyValue(filter);if(this.items){this.__debounceRender(this.__render)}}__computeFrameTime(rate){return Math.ceil(1e3/rate)}__initializeChunking(){if(this.initialCount){this.__limit=this.initialCount;this.__chunkCount=this.initialCount;this.__lastChunkTime=performance.now()}}__tryRenderChunk(){if(this.items&&this.__limit<this.items.length){this.__debounceRender(this.__requestRenderChunk)}}__requestRenderChunk(){requestAnimationFrame(()=>this.__renderChunk())}__renderChunk(){let currChunkTime=performance.now();let ratio=this._targetFrameTime/(currChunkTime-this.__lastChunkTime);this.__chunkCount=Math.round(this.__chunkCount*ratio)||1;this.__limit+=this.__chunkCount;this.__lastChunkTime=currChunkTime;this.__debounceRender(this.__render)}__observeChanged(){this.__observePaths=this.observe&&this.observe.replace(".*",".").split(" ")}__itemsChanged(change){if(this.items&&!Array.isArray(this.items)){console.warn("dom-repeat expected array for `items`, found",this.items)}if(!this.__handleItemPath(change.path,change.value)){this.__initializeChunking();this.__debounceRender(this.__render)}}__handleObservedPaths(path){if(this.__sortFn||this.__filterFn){if(!path){this.__debounceRender(this.__render,this.delay)}else if(this.__observePaths){let paths=this.__observePaths;for(let i=0;i<paths.length;i++){if(path.indexOf(paths[i])===0){this.__debounceRender(this.__render,this.delay)}}}}}__debounceRender(fn,delay=0){this.__renderDebouncer=Debouncer.debounce(this.__renderDebouncer,delay>0?timeOut.after(delay):microTask,fn.bind(this));enqueueDebouncer(this.__renderDebouncer)}render(){this.__debounceRender(this.__render);flush()}__render(){if(!this.__ensureTemplatized()){return}this.__applyFullRefresh();this.__pool.length=0;this._setRenderedItemCount(this.__instances.length);this.dispatchEvent(new CustomEvent("dom-change",{bubbles:true,composed:true}));this.__tryRenderChunk()}__applyFullRefresh(){let items=this.items||[];let isntIdxToItemsIdx=new Array(items.length);for(let i=0;i<items.length;i++){isntIdxToItemsIdx[i]=i}if(this.__filterFn){isntIdxToItemsIdx=isntIdxToItemsIdx.filter((i,idx,array)=>this.__filterFn(items[i],idx,array))}if(this.__sortFn){isntIdxToItemsIdx.sort((a,b)=>this.__sortFn(items[a],items[b]))}const itemsIdxToInstIdx=this.__itemsIdxToInstIdx={};let instIdx=0;const limit=Math.min(isntIdxToItemsIdx.length,this.__limit);for(;instIdx<limit;instIdx++){let inst=this.__instances[instIdx];let itemIdx=isntIdxToItemsIdx[instIdx];let item=items[itemIdx];itemsIdxToInstIdx[itemIdx]=instIdx;if(inst){inst._setPendingProperty(this.as,item);inst._setPendingProperty(this.indexAs,instIdx);inst._setPendingProperty(this.itemsIndexAs,itemIdx);inst._flushProperties()}else{this.__insertInstance(item,instIdx,itemIdx)}}for(let i=this.__instances.length-1;i>=instIdx;i--){this.__detachAndRemoveInstance(i)}}__detachInstance(idx){let inst=this.__instances[idx];const wrappedRoot=wrap(inst.root);for(let i=0;i<inst.children.length;i++){let el=inst.children[i];wrappedRoot.appendChild(el)}return inst}__attachInstance(idx,parent){let inst=this.__instances[idx];parent.insertBefore(inst.root,this)}__detachAndRemoveInstance(idx){let inst=this.__detachInstance(idx);if(inst){this.__pool.push(inst)}this.__instances.splice(idx,1)}__stampInstance(item,instIdx,itemIdx){let model={};model[this.as]=item;model[this.indexAs]=instIdx;model[this.itemsIndexAs]=itemIdx;return new this.__ctor(model)}__insertInstance(item,instIdx,itemIdx){let inst=this.__pool.pop();if(inst){inst._setPendingProperty(this.as,item);inst._setPendingProperty(this.indexAs,instIdx);inst._setPendingProperty(this.itemsIndexAs,itemIdx);inst._flushProperties()}else{inst=this.__stampInstance(item,instIdx,itemIdx)}let beforeRow=this.__instances[instIdx+1];let beforeNode=beforeRow?beforeRow.children[0]:this;wrap(wrap(this).parentNode).insertBefore(inst.root,beforeNode);this.__instances[instIdx]=inst;return inst}_showHideChildren(hidden){for(let i=0;i<this.__instances.length;i++){this.__instances[i]._showHideChildren(hidden)}}__handleItemPath(path,value){let itemsPath=path.slice(6);let dot=itemsPath.indexOf(".");let itemsIdx=dot<0?itemsPath:itemsPath.substring(0,dot);if(itemsIdx==parseInt(itemsIdx,10)){let itemSubPath=dot<0?"":itemsPath.substring(dot+1);this.__handleObservedPaths(itemSubPath);let instIdx=this.__itemsIdxToInstIdx[itemsIdx];let inst=this.__instances[instIdx];if(inst){let itemPath=this.as+(itemSubPath?"."+itemSubPath:"");inst._setPendingPropertyOrPath(itemPath,value,false,true);inst._flushProperties()}return true}}itemForElement(el){let instance=this.modelForElement(el);return instance&&instance[this.as]}indexForElement(el){let instance=this.modelForElement(el);return instance&&instance[this.indexAs]}modelForElement(el){return modelForElement(this.template,el)}}customElements.define(DomRepeat.is,DomRepeat);class DomIf extends PolymerElement{static get is(){return"dom-if"}static get template(){return null}static get properties(){return{if:{type:Boolean,observer:"__debounceRender"},restamp:{type:Boolean,observer:"__debounceRender"}}}constructor(){super();this.__renderDebouncer=null;this.__invalidProps=null;this.__instance=null;this._lastIf=false;this.__ctor=null;this.__hideTemplateChildren__=false}__debounceRender(){this.__renderDebouncer=Debouncer.debounce(this.__renderDebouncer,microTask,()=>this.__render());enqueueDebouncer(this.__renderDebouncer)}disconnectedCallback(){super.disconnectedCallback();const parent=wrap(this).parentNode;if(!parent||parent.nodeType==Node.DOCUMENT_FRAGMENT_NODE&&!wrap(parent).host){this.__teardownInstance()}}connectedCallback(){super.connectedCallback();this.style.display="none";if(this.if){this.__debounceRender()}}render(){flush()}__render(){if(this.if){if(!this.__ensureInstance()){return}this._showHideChildren()}else if(this.restamp){this.__teardownInstance()}if(!this.restamp&&this.__instance){this._showHideChildren()}if(this.if!=this._lastIf){this.dispatchEvent(new CustomEvent("dom-change",{bubbles:true,composed:true}));this._lastIf=this.if}}__ensureInstance(){let parentNode=wrap(this).parentNode;if(parentNode){if(!this.__ctor){let template=wrap(this).querySelector("template");if(!template){let observer=new MutationObserver(()=>{if(wrap(this).querySelector("template")){observer.disconnect();this.__render()}else{throw new Error("dom-if requires a <template> child")}});observer.observe(this,{childList:true});return false}this.__ctor=templatize(template,this,{mutableData:true,forwardHostProp:function(prop,value){if(this.__instance){if(this.if){this.__instance.forwardHostProp(prop,value)}else{this.__invalidProps=this.__invalidProps||Object.create(null);this.__invalidProps[root(prop)]=true}}}})}if(!this.__instance){this.__instance=new this.__ctor;wrap(parentNode).insertBefore(this.__instance.root,this)}else{this.__syncHostProperties();let c$=this.__instance.children;if(c$&&c$.length){let lastChild=wrap(this).previousSibling;if(lastChild!==c$[c$.length-1]){for(let i=0,n;i<c$.length&&(n=c$[i]);i++){wrap(parentNode).insertBefore(n,this)}}}}}return true}__syncHostProperties(){let props=this.__invalidProps;if(props){for(let prop in props){this.__instance._setPendingProperty(prop,this.__dataHost[prop])}this.__invalidProps=null;this.__instance._flushProperties()}}__teardownInstance(){if(this.__instance){let c$=this.__instance.children;if(c$&&c$.length){let parent=wrap(c$[0]).parentNode;if(parent){parent=wrap(parent);for(let i=0,n;i<c$.length&&(n=c$[i]);i++){parent.removeChild(n)}}}this.__instance=null;this.__invalidProps=null}}_showHideChildren(){let hidden=this.__hideTemplateChildren__||!this.if;if(this.__instance){this.__instance._showHideChildren(hidden)}}}customElements.define(DomIf.is,DomIf);let ArraySelectorMixin=dedupingMixin(superClass=>{let elementBase=ElementMixin(superClass);class ArraySelectorMixin extends elementBase{static get properties(){return{items:{type:Array},multi:{type:Boolean,value:false},selected:{type:Object,notify:true},selectedItem:{type:Object,notify:true},toggle:{type:Boolean,value:false}}}static get observers(){return["__updateSelection(multi, items.*)"]}constructor(){super();this.__lastItems=null;this.__lastMulti=null;this.__selectedMap=null}__updateSelection(multi,itemsInfo){let path=itemsInfo.path;if(path==JSCompiler_renameProperty("items",this)){let newItems=itemsInfo.base||[];let lastItems=this.__lastItems;let lastMulti=this.__lastMulti;if(multi!==lastMulti){this.clearSelection()}if(lastItems){let splices=calculateSplices(newItems,lastItems);this.__applySplices(splices)}this.__lastItems=newItems;this.__lastMulti=multi}else if(itemsInfo.path==`${JSCompiler_renameProperty("items",this)}.splices`){this.__applySplices(itemsInfo.value.indexSplices)}else{let part=path.slice(`${JSCompiler_renameProperty("items",this)}.`.length);let idx=parseInt(part,10);if(part.indexOf(".")<0&&part==idx){this.__deselectChangedIdx(idx)}}}__applySplices(splices){let selected=this.__selectedMap;for(let i=0;i<splices.length;i++){let s=splices[i];selected.forEach((idx,item)=>{if(idx<s.index);else if(idx>=s.index+s.removed.length){selected.set(item,idx+s.addedCount-s.removed.length)}else{selected.set(item,-1)}});for(let j=0;j<s.addedCount;j++){let idx=s.index+j;if(selected.has(this.items[idx])){selected.set(this.items[idx],idx)}}}this.__updateLinks();let sidx=0;selected.forEach((idx,item)=>{if(idx<0){if(this.multi){this.splice(JSCompiler_renameProperty("selected",this),sidx,1)}else{this.selected=this.selectedItem=null}selected.delete(item)}else{sidx++}})}__updateLinks(){this.__dataLinkedPaths={};if(this.multi){let sidx=0;this.__selectedMap.forEach(idx=>{if(idx>=0){this.linkPaths(`${JSCompiler_renameProperty("items",this)}.${idx}`,`${JSCompiler_renameProperty("selected",this)}.${sidx++}`)}})}else{this.__selectedMap.forEach(idx=>{this.linkPaths(JSCompiler_renameProperty("selected",this),`${JSCompiler_renameProperty("items",this)}.${idx}`);this.linkPaths(JSCompiler_renameProperty("selectedItem",this),`${JSCompiler_renameProperty("items",this)}.${idx}`)})}}clearSelection(){this.__dataLinkedPaths={};this.__selectedMap=new Map;this.selected=this.multi?[]:null;this.selectedItem=null}isSelected(item){return this.__selectedMap.has(item)}isIndexSelected(idx){return this.isSelected(this.items[idx])}__deselectChangedIdx(idx){let sidx=this.__selectedIndexForItemIndex(idx);if(sidx>=0){let i=0;this.__selectedMap.forEach((idx,item)=>{if(sidx==i++){this.deselect(item)}})}}__selectedIndexForItemIndex(idx){let selected=this.__dataLinkedPaths[`${JSCompiler_renameProperty("items",this)}.${idx}`];if(selected){return parseInt(selected.slice(`${JSCompiler_renameProperty("selected",this)}.`.length),10)}}deselect(item){let idx=this.__selectedMap.get(item);if(idx>=0){this.__selectedMap.delete(item);let sidx;if(this.multi){sidx=this.__selectedIndexForItemIndex(idx)}this.__updateLinks();if(this.multi){this.splice(JSCompiler_renameProperty("selected",this),sidx,1)}else{this.selected=this.selectedItem=null}}}deselectIndex(idx){this.deselect(this.items[idx])}select(item){this.selectIndex(this.items.indexOf(item))}selectIndex(idx){let item=this.items[idx];if(!this.isSelected(item)){if(!this.multi){this.__selectedMap.clear()}this.__selectedMap.set(item,idx);this.__updateLinks();if(this.multi){this.push(JSCompiler_renameProperty("selected",this),item)}else{this.selected=this.selectedItem=item}}else if(this.toggle){this.deselectIndex(idx)}}}return ArraySelectorMixin});let baseArraySelector=ArraySelectorMixin(PolymerElement);class ArraySelector extends baseArraySelector{static get is(){return"array-selector"}static get template(){return null}}customElements.define(ArraySelector.is,ArraySelector);const customStyleInterface=new CustomStyleInterface;if(!window.ShadyCSS){window.ShadyCSS={prepareTemplate(template,elementName,elementExtends){},prepareTemplateDom(template,elementName){},prepareTemplateStyles(template,elementName,elementExtends){},styleSubtree(element,properties){customStyleInterface.processStyles();updateNativeProperties(element,properties)},styleElement(element){customStyleInterface.processStyles()},styleDocument(properties){customStyleInterface.processStyles();updateNativeProperties(document.body,properties)},getComputedStyleValue(element,property){return getComputedStyleValue(element,property)},flushCustomStyles(){},nativeCss:nativeCssVariables,nativeShadow:nativeShadow,cssBuild:cssBuild,disableRuntime:disableRuntime}}window.ShadyCSS.CustomStyleInterface=customStyleInterface;const attr="include";const CustomStyleInterface$1=window.ShadyCSS.CustomStyleInterface;class CustomStyle extends HTMLElement{constructor(){super();this._style=null;CustomStyleInterface$1.addCustomStyle(this)}getStyle(){if(this._style){return this._style}const style=this.querySelector("style");if(!style){return null}this._style=style;const include=style.getAttribute(attr);if(include){style.removeAttribute(attr);style.textContent=cssFromModules(include)+style.textContent}if(this.ownerDocument!==window.document){window.document.head.appendChild(this)}return this._style}}window.customElements.define("custom-style",CustomStyle);let mutablePropertyChange$1;(()=>{mutablePropertyChange$1=MutableData._mutablePropertyChange})();const OptionalMutableDataBehavior={properties:{mutableData:Boolean},_shouldPropertyChange(property,value,old){return mutablePropertyChange$1(this,property,value,old,this.mutableData)}};const Base=LegacyElementMixin(HTMLElement).prototype;export{Base,Debouncer,OptionalMutableDataBehavior,Polymer,PolymerElement,TemplateInstanceBase,Templatizer,animationFrame,dashToCamelCase,dom,enqueueDebouncer,flush,gestures$1 as gestures,html,idlePeriod,matches,microTask,translate,useShadow}; \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.html b/third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.html deleted file mode 100644 index d344ec3..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.html +++ /dev/null
@@ -1,10 +0,0 @@ -<!-- -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt ---> -<script src="apply-shim.min.js"></script>
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.min.js b/third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.min.js deleted file mode 100644 index fb2ddee..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.min.js +++ /dev/null
@@ -1,32 +0,0 @@ -(function(){/* - -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -'use strict';var l=!(window.ShadyDOM&&window.ShadyDOM.inUse),p;function r(a){p=a&&a.shimcssproperties?!1:l||!(navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)||!window.CSS||!CSS.supports||!CSS.supports("box-shadow","0 0 0 var(--foo)"))}var t;window.ShadyCSS&&void 0!==window.ShadyCSS.cssBuild&&(t=window.ShadyCSS.cssBuild);var aa=!(!window.ShadyCSS||!window.ShadyCSS.disableRuntime); -window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss?p=window.ShadyCSS.nativeCss:window.ShadyCSS?(r(window.ShadyCSS),window.ShadyCSS=void 0):r(window.WebComponents&&window.WebComponents.flags);var u=p,v=t;function w(){this.end=this.start=0;this.rules=this.parent=this.previous=null;this.cssText=this.parsedCssText="";this.atRule=!1;this.type=0;this.parsedSelector=this.selector=this.keyframesName=""} -function x(a){a=a.replace(ba,"").replace(ca,"");var b=y,c=a,e=new w;e.start=0;e.end=c.length;for(var d=e,f=0,g=c.length;f<g;f++)if("{"===c[f]){d.rules||(d.rules=[]);var h=d,k=h.rules[h.rules.length-1]||null;d=new w;d.start=f+1;d.parent=h;d.previous=k;h.rules.push(d)}else"}"===c[f]&&(d.end=f+1,d=d.parent||e);return b(e,a)} -function y(a,b){var c=b.substring(a.start,a.end-1);a.parsedCssText=a.cssText=c.trim();a.parent&&(c=b.substring(a.previous?a.previous.end:a.parent.start,a.start-1),c=da(c),c=c.replace(z," "),c=c.substring(c.lastIndexOf(";")+1),c=a.parsedSelector=a.selector=c.trim(),a.atRule=0===c.indexOf("@"),a.atRule?0===c.indexOf("@media")?a.type=A:c.match(ea)&&(a.type=B,a.keyframesName=a.selector.split(z).pop()):a.type=0===c.indexOf("--")?C:D);if(c=a.rules)for(var e=0,d=c.length,f=void 0;e<d&&(f=c[e]);e++)y(f,b); -return a}function da(a){return a.replace(/\\([0-9a-f]{1,6})\s/gi,function(a,c){a=c;for(c=6-a.length;c--;)a="0"+a;return"\\"+a})} -function E(a,b,c){c=void 0===c?"":c;var e="";if(a.cssText||a.rules){var d=a.rules,f;if(f=d)f=d[0],f=!(f&&f.selector&&0===f.selector.indexOf("--"));if(f){f=0;for(var g=d.length,h=void 0;f<g&&(h=d[f]);f++)e=E(h,b,e)}else b?b=a.cssText:(b=a.cssText,b=b.replace(fa,"").replace(ha,""),b=b.replace(ia,"").replace(ja,"")),(e=b.trim())&&(e=" "+e+"\n")}e&&(a.selector&&(c+=a.selector+" {\n"),c+=e,a.selector&&(c+="}\n\n"));return c} -var D=1,B=7,A=4,C=1E3,ba=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,ca=/@import[^;]*;/gim,fa=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,ha=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,ia=/@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,ja=/[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,ea=/^@[^\s]*keyframes/,z=/\s+/g;var G=/(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi,H=/(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi,ka=/@media\s(.*)/;var I=new Set;function J(a){if(!a)return"";"string"===typeof a&&(a=x(a));return E(a,u)}function K(a){!a.__cssRules&&a.textContent&&(a.__cssRules=x(a.textContent));return a.__cssRules||null}function L(a,b,c,e){if(a){var d=!1,f=a.type;if(e&&f===A){var g=a.selector.match(ka);g&&(window.matchMedia(g[1]).matches||(d=!0))}f===D?b(a):c&&f===B?c(a):f===C&&(d=!0);if((a=a.rules)&&!d)for(d=0,f=a.length,g=void 0;d<f&&(g=a[d]);d++)L(g,b,c,e)}} -function M(a,b){var c=a.indexOf("var(");if(-1===c)return b(a,"","","");a:{var e=0;var d=c+3;for(var f=a.length;d<f;d++)if("("===a[d])e++;else if(")"===a[d]&&0===--e)break a;d=-1}e=a.substring(c+4,d);c=a.substring(0,c);a=M(a.substring(d+1),b);d=e.indexOf(",");return-1===d?b(c,e.trim(),"",a):b(c,e.substring(0,d).trim(),e.substring(d+1).trim(),a)} -function N(a){if(void 0!==v)return v;if(void 0===a.__cssBuild){var b=a.getAttribute("css-build");if(b)a.__cssBuild=b;else{a:{b="template"===a.localName?a.content.firstChild:a.firstChild;if(b instanceof Comment&&(b=b.textContent.trim().split(":"),"css-build"===b[0])){b=b[1];break a}b=""}if(""!==b){var c="template"===a.localName?a.content.firstChild:a.firstChild;c.parentNode.removeChild(c)}a.__cssBuild=b}}return a.__cssBuild||""};var la=/;\s*/m,ma=/^\s*(initial)|(inherit)\s*$/,O=/\s*!important/;function P(){this.a={}}P.prototype.set=function(a,b){a=a.trim();this.a[a]={h:b,i:{}}};P.prototype.get=function(a){a=a.trim();return this.a[a]||null};var Q=null;function R(){this.b=this.c=null;this.a=new P}R.prototype.o=function(a){a=H.test(a)||G.test(a);H.lastIndex=0;G.lastIndex=0;return a}; -R.prototype.m=function(a,b){if(void 0===a._gatheredStyle){var c=[];for(var e=a.content.querySelectorAll("style"),d=0;d<e.length;d++){var f=e[d];if(f.hasAttribute("shady-unscoped")){if(!l){var g=f.textContent;I.has(g)||(I.add(g),g=f.cloneNode(!0),document.head.appendChild(g));f.parentNode.removeChild(f)}}else c.push(f.textContent),f.parentNode.removeChild(f)}(c=c.join("").trim())?(e=document.createElement("style"),e.textContent=c,a.content.insertBefore(e,a.content.firstChild),c=e):c=null;a._gatheredStyle= -c}return(a=a._gatheredStyle)?this.j(a,b):null};R.prototype.j=function(a,b){b=void 0===b?"":b;var c=K(a);this.l(c,b);a.textContent=J(c);return c};R.prototype.f=function(a){var b=this,c=K(a);L(c,function(a){":root"===a.selector&&(a.selector="html");b.g(a)});a.textContent=J(c);return c};R.prototype.l=function(a,b){var c=this;this.c=b;L(a,function(a){c.g(a)});this.c=null};R.prototype.g=function(a){a.cssText=na(this,a.parsedCssText,a);":root"===a.selector&&(a.selector=":host > *")}; -function na(a,b,c){b=b.replace(G,function(b,d,f,g){return oa(a,b,d,f,g,c)});return S(a,b,c)}function pa(a,b){for(var c=b;c.parent;)c=c.parent;var e={},d=!1;L(c,function(c){(d=d||c===b)||c.selector===b.selector&&Object.assign(e,T(a,c.parsedCssText))});return e} -function S(a,b,c){for(var e;e=H.exec(b);){var d=e[0],f=e[1];e=e.index;var g=b.slice(0,e+d.indexOf("@apply"));b=b.slice(e+d.length);var h=c?pa(a,c):{};Object.assign(h,T(a,g));d=void 0;var k=a;f=f.replace(la,"");var n=[];var m=k.a.get(f);m||(k.a.set(f,{}),m=k.a.get(f));if(m){k.c&&(m.i[k.c]=!0);var q=m.h;for(d in q)k=h&&h[d],m=[d,": var(",f,"_-_",d],k&&m.push(",",k.replace(O,"")),m.push(")"),O.test(q[d])&&m.push(" !important"),n.push(m.join(""))}d=n.join("; ");b=g+d+b;H.lastIndex=e+d.length}return b} -function T(a,b,c){c=void 0===c?!1:c;b=b.split(";");for(var e,d,f={},g=0,h;g<b.length;g++)if(e=b[g])if(h=e.split(":"),1<h.length){e=h[0].trim();d=h.slice(1).join(":");if(c){var k=a;h=e;var n=ma.exec(d);n&&(n[1]?(k.b||(k.b=document.createElement("meta"),k.b.setAttribute("apply-shim-measure",""),k.b.style.all="initial",document.head.appendChild(k.b)),h=window.getComputedStyle(k.b).getPropertyValue(h)):h="apply-shim-inherit",d=h)}f[e]=d}return f}function qa(a,b){if(Q)for(var c in b.i)c!==a.c&&Q(c)} -function oa(a,b,c,e,d,f){e&&M(e,function(b,c){c&&a.a.get(c)&&(d="@apply "+c+";")});if(!d)return b;var g=S(a,""+d,f);f=b.slice(0,b.indexOf("--"));var h=g=T(a,g,!0),k=a.a.get(c),n=k&&k.h;n?h=Object.assign(Object.create(n),g):a.a.set(c,h);var m=[],q,Z=!1;for(q in h){var F=g[q];void 0===F&&(F="initial");!n||q in n||(Z=!0);m.push(c+"_-_"+q+": "+F)}Z&&qa(a,k);k&&(k.h=h);e&&(f=b+";"+f);return f+m.join("; ")+";"}R.prototype.detectMixin=R.prototype.o;R.prototype.transformStyle=R.prototype.j; -R.prototype.transformCustomStyle=R.prototype.f;R.prototype.transformRules=R.prototype.l;R.prototype.transformRule=R.prototype.g;R.prototype.transformTemplate=R.prototype.m;R.prototype._separator="_-_";Object.defineProperty(R.prototype,"invalidCallback",{get:function(){return Q},set:function(a){Q=a}});var U={};var ra=Promise.resolve();function sa(a){if(a=U[a])a._applyShimCurrentVersion=a._applyShimCurrentVersion||0,a._applyShimValidatingVersion=a._applyShimValidatingVersion||0,a._applyShimNextVersion=(a._applyShimNextVersion||0)+1}function ta(a){return a._applyShimCurrentVersion===a._applyShimNextVersion}function ua(a){a._applyShimValidatingVersion=a._applyShimNextVersion;a._validating||(a._validating=!0,ra.then(function(){a._applyShimCurrentVersion=a._applyShimNextVersion;a._validating=!1}))};var V=new R;function W(){this.a=null;V.invalidCallback=sa}function X(a){!a.a&&window.ShadyCSS.CustomStyleInterface&&(a.a=window.ShadyCSS.CustomStyleInterface,a.a.transformCallback=function(a){V.f(a)},a.a.validateCallback=function(){requestAnimationFrame(function(){a.a.enqueued&&a.flushCustomStyles()})})}W.prototype.prepareTemplate=function(a,b){X(this);""===N(a)&&(U[b]=a,b=V.m(a,b),a._styleAst=b)}; -W.prototype.flushCustomStyles=function(){X(this);if(this.a){var a=this.a.processStyles();if(this.a.enqueued){for(var b=0;b<a.length;b++){var c=this.a.getStyleForCustomStyle(a[b]);c&&V.f(c)}this.a.enqueued=!1}}}; -W.prototype.styleSubtree=function(a,b){X(this);if(b)for(var c in b)null===c?a.style.removeProperty(c):a.style.setProperty(c,b[c]);if(a.shadowRoot)for(this.styleElement(a),a=a.shadowRoot.children||a.shadowRoot.childNodes,b=0;b<a.length;b++)this.styleSubtree(a[b]);else for(a=a.children||a.childNodes,b=0;b<a.length;b++)this.styleSubtree(a[b])}; -W.prototype.styleElement=function(a){X(this);var b=a.localName,c;b?-1<b.indexOf("-")?c=b:c=a.getAttribute&&a.getAttribute("is")||"":c=a.is;b=U[c];if(!(b&&""!==N(b)||!b||ta(b))){if(ta(b)||b._applyShimValidatingVersion!==b._applyShimNextVersion)this.prepareTemplate(b,c),ua(b);if(a=a.shadowRoot)if(a=a.querySelector("style"))a.__cssRules=b._styleAst,a.textContent=J(b._styleAst)}};W.prototype.styleDocument=function(a){X(this);this.styleSubtree(document.body,a)}; -if(!window.ShadyCSS||!window.ShadyCSS.ScopingShim){var Y=new W,va=window.ShadyCSS&&window.ShadyCSS.CustomStyleInterface;window.ShadyCSS={prepareTemplate:function(a,b){Y.flushCustomStyles();Y.prepareTemplate(a,b)},prepareTemplateStyles:function(a,b,c){window.ShadyCSS.prepareTemplate(a,b,c)},prepareTemplateDom:function(){},styleSubtree:function(a,b){Y.flushCustomStyles();Y.styleSubtree(a,b)},styleElement:function(a){Y.flushCustomStyles();Y.styleElement(a)},styleDocument:function(a){Y.flushCustomStyles(); -Y.styleDocument(a)},getComputedStyleValue:function(a,b){return(a=window.getComputedStyle(a).getPropertyValue(b))?a.trim():""},flushCustomStyles:function(){Y.flushCustomStyles()},nativeCss:u,nativeShadow:l,cssBuild:v,disableRuntime:aa};va&&(window.ShadyCSS.CustomStyleInterface=va)}window.ShadyCSS.ApplyShim=V;}).call(this); - -//# sourceMappingURL=apply-shim.min.js.map
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.html b/third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.html deleted file mode 100644 index a3919fa..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.html +++ /dev/null
@@ -1,10 +0,0 @@ -<!-- -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt ---> -<script src="custom-style-interface.min.js"></script>
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.min.js b/third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.min.js deleted file mode 100644 index fb31c84..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.min.js +++ /dev/null
@@ -1,15 +0,0 @@ -(function(){/* - -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -'use strict';var c=null,f=window.HTMLImports&&window.HTMLImports.whenReady||null,g;function h(a){requestAnimationFrame(function(){f?f(a):(c||(c=new Promise(function(a){g=a}),"complete"===document.readyState?g():document.addEventListener("readystatechange",function(){"complete"===document.readyState&&g()})),c.then(function(){a&&a()}))})};var k=null,l=null;function m(){this.customStyles=[];this.enqueued=!1;h(function(){window.ShadyCSS.flushCustomStyles&&window.ShadyCSS.flushCustomStyles()})}function n(a){!a.enqueued&&l&&(a.enqueued=!0,h(l))}m.prototype.c=function(a){a.__seenByShadyCSS||(a.__seenByShadyCSS=!0,this.customStyles.push(a),n(this))};m.prototype.b=function(a){if(a.__shadyCSSCachedStyle)return a.__shadyCSSCachedStyle;var b;a.getStyle?b=a.getStyle():b=a;return b}; -m.prototype.a=function(){for(var a=this.customStyles,b=0;b<a.length;b++){var d=a[b];if(!d.__shadyCSSCachedStyle){var e=this.b(d);e&&(e=e.__appliedElement||e,k&&k(e),d.__shadyCSSCachedStyle=e)}}return a};m.prototype.addCustomStyle=m.prototype.c;m.prototype.getStyleForCustomStyle=m.prototype.b;m.prototype.processStyles=m.prototype.a; -Object.defineProperties(m.prototype,{transformCallback:{get:function(){return k},set:function(a){k=a}},validateCallback:{get:function(){return l},set:function(a){var b=!1;l||(b=!0);l=a;b&&n(this)}}});function p(a,b){for(var d in b)null===d?a.style.removeProperty(d):a.style.setProperty(d,b[d])};var q=!(window.ShadyDOM&&window.ShadyDOM.inUse),r;function t(a){r=a&&a.shimcssproperties?!1:q||!(navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)||!window.CSS||!CSS.supports||!CSS.supports("box-shadow","0 0 0 var(--foo)"))}var u;window.ShadyCSS&&void 0!==window.ShadyCSS.cssBuild&&(u=window.ShadyCSS.cssBuild);var v=!(!window.ShadyCSS||!window.ShadyCSS.disableRuntime); -window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss?r=window.ShadyCSS.nativeCss:window.ShadyCSS?(t(window.ShadyCSS),window.ShadyCSS=void 0):t(window.WebComponents&&window.WebComponents.flags);var w=r,x=u;var y=new m;window.ShadyCSS||(window.ShadyCSS={prepareTemplate:function(){},prepareTemplateDom:function(){},prepareTemplateStyles:function(){},styleSubtree:function(a,b){y.a();p(a,b)},styleElement:function(){y.a()},styleDocument:function(a){y.a();p(document.body,a)},getComputedStyleValue:function(a,b){return(a=window.getComputedStyle(a).getPropertyValue(b))?a.trim():""},flushCustomStyles:function(){},nativeCss:w,nativeShadow:q,cssBuild:x,disableRuntime:v});window.ShadyCSS.CustomStyleInterface=y;}).call(this); - -//# sourceMappingURL=custom-style-interface.min.js.map
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/apply-shim.js b/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/apply-shim.js deleted file mode 100644 index 09ba034..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/apply-shim.js +++ /dev/null
@@ -1,223 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -import ApplyShim from '../src/apply-shim.js'; -import templateMap from '../src/template-map.js'; -import {getIsExtends, toCssText, elementHasBuiltCss} from '../src/style-util.js'; -import * as ApplyShimUtils from '../src/apply-shim-utils.js'; -import {getComputedStyleValue, updateNativeProperties} from '../src/common-utils.js'; -import {CustomStyleInterfaceInterface} from '../src/custom-style-interface.js'; // eslint-disable-line no-unused-vars -import {nativeCssVariables, nativeShadow, cssBuild, disableRuntime} from '../src/style-settings.js'; - -/** @const {ApplyShim} */ -const applyShim = new ApplyShim(); - -class ApplyShimInterface { - constructor() { - /** @type {?CustomStyleInterfaceInterface} */ - this.customStyleInterface = null; - applyShim['invalidCallback'] = ApplyShimUtils.invalidate; - } - ensure() { - if (this.customStyleInterface) { - return; - } - if (window.ShadyCSS.CustomStyleInterface) { - this.customStyleInterface = - /** @type {!CustomStyleInterfaceInterface} */ ( - window.ShadyCSS.CustomStyleInterface); - this.customStyleInterface['transformCallback'] = (style) => { - applyShim.transformCustomStyle(style); - }; - this.customStyleInterface['validateCallback'] = () => { - requestAnimationFrame(() => { - if (this.customStyleInterface['enqueued']) { - this.flushCustomStyles(); - } - }); - } - } - } - /** - * @param {!HTMLTemplateElement} template - * @param {string} elementName - */ - prepareTemplate(template, elementName) { - this.ensure(); - if (elementHasBuiltCss(template)) { - return; - } - templateMap[elementName] = template; - let ast = applyShim.transformTemplate(template, elementName); - // save original style ast to use for revalidating instances - template['_styleAst'] = ast; - } - flushCustomStyles() { - this.ensure(); - if (!this.customStyleInterface) { - return; - } - let styles = this.customStyleInterface['processStyles'](); - if (!this.customStyleInterface['enqueued']) { - return; - } - for (let i = 0; i < styles.length; i++ ) { - let cs = styles[i]; - let style = this.customStyleInterface['getStyleForCustomStyle'](cs); - if (style) { - applyShim.transformCustomStyle(style); - } - } - this.customStyleInterface['enqueued'] = false; - } - /** - * @param {HTMLElement} element - * @param {Object=} properties - */ - styleSubtree(element, properties) { - this.ensure(); - if (properties) { - updateNativeProperties(element, properties); - } - if (element.shadowRoot) { - this.styleElement(element); - let shadowChildren = - /** @type {!ParentNode} */ (element.shadowRoot).children || - element.shadowRoot.childNodes; - for (let i = 0; i < shadowChildren.length; i++) { - this.styleSubtree(/** @type {HTMLElement} */(shadowChildren[i])); - } - } else { - let children = element.children || element.childNodes; - for (let i = 0; i < children.length; i++) { - this.styleSubtree(/** @type {HTMLElement} */(children[i])); - } - } - } - /** - * @param {HTMLElement} element - */ - styleElement(element) { - this.ensure(); - let {is} = getIsExtends(element); - let template = templateMap[is]; - if (template && elementHasBuiltCss(template)) { - return; - } - if (template && !ApplyShimUtils.templateIsValid(template)) { - // only revalidate template once - if (!ApplyShimUtils.templateIsValidating(template)) { - this.prepareTemplate(template, is); - ApplyShimUtils.startValidatingTemplate(template); - } - // update this element instance - let root = element.shadowRoot; - if (root) { - let style = /** @type {HTMLStyleElement} */(root.querySelector('style')); - if (style) { - // reuse the template's style ast, it has all the original css text - style['__cssRules'] = template['_styleAst']; - style.textContent = toCssText(template['_styleAst']) - } - } - } - } - /** - * @param {Object=} properties - */ - styleDocument(properties) { - this.ensure(); - this.styleSubtree(document.body, properties); - } -} - -if (!window.ShadyCSS || !window.ShadyCSS.ScopingShim) { - const applyShimInterface = new ApplyShimInterface(); - let CustomStyleInterface = window.ShadyCSS && window.ShadyCSS.CustomStyleInterface; - - /** @suppress {duplicate} */ - window.ShadyCSS = { - /** - * @param {!HTMLTemplateElement} template - * @param {string} elementName - * @param {string=} elementExtends - */ - prepareTemplate(template, elementName, elementExtends) { // eslint-disable-line no-unused-vars - applyShimInterface.flushCustomStyles(); - applyShimInterface.prepareTemplate(template, elementName); - }, - - /** - * @param {!HTMLTemplateElement} template - * @param {string} elementName - * @param {string=} elementExtends - */ - prepareTemplateStyles(template, elementName, elementExtends) { - window.ShadyCSS.prepareTemplate(template, elementName, elementExtends); - }, - - /** - * @param {!HTMLTemplateElement} template - * @param {string} elementName - */ - prepareTemplateDom(template, elementName) {}, // eslint-disable-line no-unused-vars - - /** - * @param {!HTMLElement} element - * @param {Object=} properties - */ - styleSubtree(element, properties) { - applyShimInterface.flushCustomStyles(); - applyShimInterface.styleSubtree(element, properties); - }, - - /** - * @param {!HTMLElement} element - */ - styleElement(element) { - applyShimInterface.flushCustomStyles(); - applyShimInterface.styleElement(element); - }, - - /** - * @param {Object=} properties - */ - styleDocument(properties) { - applyShimInterface.flushCustomStyles(); - applyShimInterface.styleDocument(properties); - }, - - /** - * @param {Element} element - * @param {string} property - * @return {string} - */ - getComputedStyleValue(element, property) { - return getComputedStyleValue(element, property); - }, - - flushCustomStyles() { - applyShimInterface.flushCustomStyles(); - }, - - nativeCss: nativeCssVariables, - nativeShadow: nativeShadow, - cssBuild: cssBuild, - disableRuntime: disableRuntime, - }; - - if (CustomStyleInterface) { - window.ShadyCSS.CustomStyleInterface = CustomStyleInterface; - } -} - -window.ShadyCSS.ApplyShim = applyShim;
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/custom-style-interface.js b/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/custom-style-interface.js deleted file mode 100644 index bf5024a..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/custom-style-interface.js +++ /dev/null
@@ -1,82 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -import CustomStyleInterface from '../src/custom-style-interface.js'; -import {getComputedStyleValue, updateNativeProperties} from '../src/common-utils.js'; -import {nativeCssVariables, nativeShadow, cssBuild, disableRuntime} from '../src/style-settings.js'; - -const customStyleInterface = new CustomStyleInterface(); - -if (!window.ShadyCSS) { - window.ShadyCSS = { - /** - * @param {!HTMLTemplateElement} template - * @param {string} elementName - * @param {string=} elementExtends - */ - prepareTemplate(template, elementName, elementExtends) {}, // eslint-disable-line no-unused-vars - - /** - * @param {!HTMLTemplateElement} template - * @param {string} elementName - */ - prepareTemplateDom(template, elementName) {}, // eslint-disable-line no-unused-vars - - /** - * @param {!HTMLTemplateElement} template - * @param {string} elementName - * @param {string=} elementExtends - */ - prepareTemplateStyles(template, elementName, elementExtends) {}, // eslint-disable-line no-unused-vars - - /** - * @param {Element} element - * @param {Object=} properties - */ - styleSubtree(element, properties) { - customStyleInterface.processStyles(); - updateNativeProperties(element, properties); - }, - - /** - * @param {Element} element - */ - styleElement(element) { // eslint-disable-line no-unused-vars - customStyleInterface.processStyles(); - }, - - /** - * @param {Object=} properties - */ - styleDocument(properties) { - customStyleInterface.processStyles(); - updateNativeProperties(document.body, properties); - }, - - /** - * @param {Element} element - * @param {string} property - * @return {string} - */ - getComputedStyleValue(element, property) { - return getComputedStyleValue(element, property); - }, - - flushCustomStyles() {}, - nativeCss: nativeCssVariables, - nativeShadow: nativeShadow, - cssBuild: cssBuild, - disableRuntime: disableRuntime, - } -} - -window.ShadyCSS.CustomStyleInterface = customStyleInterface; \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/scoping-shim.js b/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/scoping-shim.js deleted file mode 100644 index b3b8982..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/scoping-shim.js +++ /dev/null
@@ -1,108 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -import ScopingShim from '../src/scoping-shim.js'; -import {nativeCssVariables, nativeShadow, cssBuild, disableRuntime} from '../src/style-settings.js'; - -/** @const {ScopingShim} */ -const scopingShim = new ScopingShim(); - -let ApplyShim, CustomStyleInterface; - -if (window['ShadyCSS']) { - ApplyShim = window['ShadyCSS']['ApplyShim']; - CustomStyleInterface = window['ShadyCSS']['CustomStyleInterface']; -} - -window.ShadyCSS = { - ScopingShim: scopingShim, - /** - * @param {!HTMLTemplateElement} template - * @param {string} elementName - * @param {string=} elementExtends - */ - prepareTemplate(template, elementName, elementExtends) { - scopingShim.flushCustomStyles(); - scopingShim.prepareTemplate(template, elementName, elementExtends) - }, - - /** - * @param {!HTMLTemplateElement} template - * @param {string} elementName - */ - prepareTemplateDom(template, elementName) { - scopingShim.prepareTemplateDom(template, elementName); - }, - - /** - * @param {!HTMLTemplateElement} template - * @param {string} elementName - * @param {string=} elementExtends - */ - prepareTemplateStyles(template, elementName, elementExtends) { - scopingShim.flushCustomStyles(); - scopingShim.prepareTemplateStyles(template, elementName, elementExtends) - }, - /** - * @param {!HTMLElement} element - * @param {Object=} properties - */ - styleSubtree(element, properties) { - scopingShim.flushCustomStyles(); - scopingShim.styleSubtree(element, properties); - }, - - /** - * @param {!HTMLElement} element - */ - styleElement(element) { - scopingShim.flushCustomStyles(); - scopingShim.styleElement(element); - }, - - /** - * @param {Object=} properties - */ - styleDocument(properties) { - scopingShim.flushCustomStyles(); - scopingShim.styleDocument(properties); - }, - - flushCustomStyles() { - scopingShim.flushCustomStyles(); - }, - - /** - * @param {Element} element - * @param {string} property - * @return {string} - */ - getComputedStyleValue(element, property) { - return scopingShim.getComputedStyleValue(element, property); - }, - - nativeCss: nativeCssVariables, - - nativeShadow: nativeShadow, - - cssBuild: cssBuild, - - disableRuntime: disableRuntime, -}; - -if (ApplyShim) { - window.ShadyCSS.ApplyShim = ApplyShim; -} - -if (CustomStyleInterface) { - window.ShadyCSS.CustomStyleInterface = CustomStyleInterface; -} \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/externs/shadycss-externs.js b/third_party/polymer/v3_0/components-chromium/shadycss/externs/shadycss-externs.js deleted file mode 100644 index 629a24c..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/externs/shadycss-externs.js +++ /dev/null
@@ -1,66 +0,0 @@ -/** @externs */ - -/** @typedef {{ - * styleElement: function(!HTMLElement), - * styleSubtree: function(!HTMLElement, Object<string, string>=), - * prepareTemplate: function(!HTMLTemplateElement, string, string=), - * prepareTemplateStyles: function(!HTMLTemplateElement, string, string=), - * prepareTemplateDom: function(!HTMLTemplateElement, string), - * styleDocument: function(Object<string, string>=), - * flushCustomStyles: function(), - * getComputedStyleValue: function(!Element, string): string, - * ScopingShim: (Object|undefined), - * ApplyShim: (Object|undefined), - * CustomStyleInterface: (Object|undefined), - * nativeCss: boolean, - * nativeShadow: boolean, - * cssBuild: (string | undefined), - * disableRuntime: boolean, - * }} - */ -let ShadyCSSInterface; //eslint-disable-line no-unused-vars - -/** - * @typedef {{ - * shimcssproperties: (boolean | undefined), - * shimshadow: (boolean | undefined), - * cssBuild: (string | undefined), - * disableRuntime: (boolean | undefined), - * }} - */ -let ShadyCSSOptions; //eslint-disable-line no-unused-vars - -/** @type {(ShadyCSSInterface | ShadyCSSOptions | undefined)} */ -window.ShadyCSS; - -/** @type {string|undefined} */ -Element.prototype.extends; - -/** @type {?Element|undefined} */ -Element.prototype._element; - -/** @type {string|undefined} */ -Element.prototype.__cssBuild; - -/** @type {boolean|undefined} */ -HTMLTemplateElement.prototype._validating; - -/** @type {boolean|undefined} */ -HTMLTemplateElement.prototype._prepared; - -/** @type {boolean|undefined} */ -HTMLTemplateElement.prototype._domPrepared; - -/** @type {?DocumentFragment|undefined} */ -HTMLTemplateElement.prototype._content; - -/** @type {?HTMLStyleElement|undefined} */ -HTMLTemplateElement.prototype._gatheredStyle; - -/** @type {?HTMLStyleElement|undefined} */ -HTMLTemplateElement.prototype._style; - -/** - * @type {string | undefined} - */ -DOMTokenList.prototype.value; \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/scoping-shim.min.js b/third_party/polymer/v3_0/components-chromium/shadycss/scoping-shim.min.js deleted file mode 100644 index 36d02906..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/scoping-shim.min.js +++ /dev/null
@@ -1,59 +0,0 @@ -(function(){/* - -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -'use strict';var k,aa="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this;function n(){this.end=this.start=0;this.rules=this.parent=this.previous=null;this.cssText=this.parsedCssText="";this.atRule=!1;this.type=0;this.parsedSelector=this.selector=this.keyframesName=""} -function p(a){a=a.replace(ba,"").replace(ca,"");var b=da,c=a,e=new n;e.start=0;e.end=c.length;for(var d=e,f=0,g=c.length;f<g;f++)if("{"===c[f]){d.rules||(d.rules=[]);var h=d,l=h.rules[h.rules.length-1]||null;d=new n;d.start=f+1;d.parent=h;d.previous=l;h.rules.push(d)}else"}"===c[f]&&(d.end=f+1,d=d.parent||e);return b(e,a)} -function da(a,b){var c=b.substring(a.start,a.end-1);a.parsedCssText=a.cssText=c.trim();a.parent&&(c=b.substring(a.previous?a.previous.end:a.parent.start,a.start-1),c=ea(c),c=c.replace(fa," "),c=c.substring(c.lastIndexOf(";")+1),c=a.parsedSelector=a.selector=c.trim(),a.atRule=0===c.indexOf("@"),a.atRule?0===c.indexOf("@media")?a.type=ha:c.match(ia)&&(a.type=q,a.keyframesName=a.selector.split(fa).pop()):a.type=0===c.indexOf("--")?ja:ka);if(c=a.rules)for(var e=0,d=c.length,f=void 0;e<d&&(f=c[e]);e++)da(f, -b);return a}function ea(a){return a.replace(/\\([0-9a-f]{1,6})\s/gi,function(a,c){a=c;for(c=6-a.length;c--;)a="0"+a;return"\\"+a})} -function la(a,b,c){c=void 0===c?"":c;var e="";if(a.cssText||a.rules){var d=a.rules,f;if(f=d)f=d[0],f=!(f&&f.selector&&0===f.selector.indexOf("--"));if(f){f=0;for(var g=d.length,h=void 0;f<g&&(h=d[f]);f++)e=la(h,b,e)}else b?b=a.cssText:(b=a.cssText,b=b.replace(ma,"").replace(na,""),b=b.replace(oa,"").replace(pa,"")),(e=b.trim())&&(e=" "+e+"\n")}e&&(a.selector&&(c+=a.selector+" {\n"),c+=e,a.selector&&(c+="}\n\n"));return c} -var ka=1,q=7,ha=4,ja=1E3,ba=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,ca=/@import[^;]*;/gim,ma=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim,na=/(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim,oa=/@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim,pa=/[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim,ia=/^@[^\s]*keyframes/,fa=/\s+/g;var r=!(window.ShadyDOM&&window.ShadyDOM.inUse),t;function qa(a){t=a&&a.shimcssproperties?!1:r||!(navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/)||!window.CSS||!CSS.supports||!CSS.supports("box-shadow","0 0 0 var(--foo)"))}var ra;window.ShadyCSS&&void 0!==window.ShadyCSS.cssBuild&&(ra=window.ShadyCSS.cssBuild);var u=!(!window.ShadyCSS||!window.ShadyCSS.disableRuntime); -window.ShadyCSS&&void 0!==window.ShadyCSS.nativeCss?t=window.ShadyCSS.nativeCss:window.ShadyCSS?(qa(window.ShadyCSS),window.ShadyCSS=void 0):qa(window.WebComponents&&window.WebComponents.flags);var v=t,w=ra;var x=/(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi,y=/(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi,sa=/(--[\w-]+)\s*([:,;)]|$)/gi,ta=/(animation\s*:)|(animation-name\s*:)/,ua=/@media\s(.*)/,va=/\{[^}]*\}/g;var wa=new Set;function z(a,b){if(!a)return"";"string"===typeof a&&(a=p(a));b&&A(a,b);return la(a,v)}function B(a){!a.__cssRules&&a.textContent&&(a.__cssRules=p(a.textContent));return a.__cssRules||null}function xa(a){return!!a.parent&&a.parent.type===q}function A(a,b,c,e){if(a){var d=!1,f=a.type;if(e&&f===ha){var g=a.selector.match(ua);g&&(window.matchMedia(g[1]).matches||(d=!0))}f===ka?b(a):c&&f===q?c(a):f===ja&&(d=!0);if((a=a.rules)&&!d)for(d=0,f=a.length,g=void 0;d<f&&(g=a[d]);d++)A(g,b,c,e)}} -function C(a,b,c,e){var d=document.createElement("style");b&&d.setAttribute("scope",b);d.textContent=a;ya(d,c,e);return d}var D=null;function za(a){a=document.createComment(" Shady DOM styles for "+a+" ");var b=document.head;b.insertBefore(a,(D?D.nextSibling:null)||b.firstChild);return D=a}function ya(a,b,c){b=b||document.head;b.insertBefore(a,c&&c.nextSibling||b.firstChild);D?a.compareDocumentPosition(D)===Node.DOCUMENT_POSITION_PRECEDING&&(D=a):D=a} -function Aa(a,b){for(var c=0,e=a.length;b<e;b++)if("("===a[b])c++;else if(")"===a[b]&&0===--c)return b;return-1}function Ba(a,b){var c=a.indexOf("var(");if(-1===c)return b(a,"","","");var e=Aa(a,c+3),d=a.substring(c+4,e);c=a.substring(0,c);a=Ba(a.substring(e+1),b);e=d.indexOf(",");return-1===e?b(c,d.trim(),"",a):b(c,d.substring(0,e).trim(),d.substring(e+1).trim(),a)}function E(a,b){r?a.setAttribute("class",b):window.ShadyDOM.nativeMethods.setAttribute.call(a,"class",b)} -var F=window.ShadyDOM&&window.ShadyDOM.wrap||function(a){return a};function G(a){var b=a.localName,c="";b?-1<b.indexOf("-")||(c=b,b=a.getAttribute&&a.getAttribute("is")||""):(b=a.is,c=a.extends);return{is:b,s:c}}function Ca(a){for(var b=[],c="",e=0;0<=e&&e<a.length;e++)if("("===a[e]){var d=Aa(a,e);c+=a.slice(e,d+1);e=d}else","===a[e]?(b.push(c),c=""):c+=a[e];c&&b.push(c);return b} -function H(a){if(void 0!==w)return w;if(void 0===a.__cssBuild){var b=a.getAttribute("css-build");if(b)a.__cssBuild=b;else{a:{b="template"===a.localName?a.content.firstChild:a.firstChild;if(b instanceof Comment&&(b=b.textContent.trim().split(":"),"css-build"===b[0])){b=b[1];break a}b=""}if(""!==b){var c="template"===a.localName?a.content.firstChild:a.firstChild;c.parentNode.removeChild(c)}a.__cssBuild=b}}return a.__cssBuild||""} -function Da(a){a=void 0===a?"":a;return""!==a&&v?r?"shadow"===a:"shady"===a:!1};function I(){}function Ea(a,b){J(K,a,function(a){L(a,b||"")})}function J(a,b,c){b.nodeType===Node.ELEMENT_NODE&&c(b);var e;"template"===b.localName?e=(b.content||b._content||b).childNodes:e=b.children||b.childNodes;if(e)for(b=0;b<e.length;b++)J(a,e[b],c)} -function L(a,b,c){if(b)if(a.classList)c?(a.classList.remove("style-scope"),a.classList.remove(b)):(a.classList.add("style-scope"),a.classList.add(b));else if(a.getAttribute){var e=a.getAttribute("class");c?e&&(b=e.replace("style-scope","").replace(b,""),E(a,b)):E(a,(e?e+" ":"")+"style-scope "+b)}}function Fa(a,b,c){J(K,a,function(a){L(a,b,!0);L(a,c)})}function Ga(a,b){J(K,a,function(a){L(a,b||"",!0)})} -function M(a,b,c,e,d){var f=K;d=void 0===d?"":d;""===d&&(r||"shady"===(void 0===e?"":e)?d=z(b,c):(a=G(a),d=Ha(f,b,a.is,a.s,c)+"\n\n"));return d.trim()}function Ha(a,b,c,e,d){var f=Ia(c,e);c=c?"."+c:"";return z(b,function(b){b.c||(b.selector=b.g=Ja(a,b,a.b,c,f),b.c=!0);d&&d(b,c,f)})}function Ia(a,b){return b?"[is="+a+"]":a}function Ja(a,b,c,e,d){var f=Ca(b.selector);if(!xa(b)){b=0;for(var g=f.length,h=void 0;b<g&&(h=f[b]);b++)f[b]=c.call(a,h,e,d)}return f.filter(function(a){return!!a}).join(",")} -function Ka(a){return a.replace(La,function(a,c,e){-1<e.indexOf("+")?e=e.replace(/\+/g,"___"):-1<e.indexOf("___")&&(e=e.replace(/___/g,"+"));return":"+c+"("+e+")"})}function Ma(a){for(var b=[],c;c=a.match(Na);){var e=c.index,d=Aa(a,e);if(-1===d)throw Error(c.input+" selector missing ')'");c=a.slice(e,d+1);a=a.replace(c,"\ue000");b.push(c)}return{A:a,matches:b}}function Oa(a,b){var c=a.split("\ue000");return b.reduce(function(a,b,f){return a+b+c[f+1]},c[0])} -I.prototype.b=function(a,b,c){var e=!1;a=a.trim();var d=La.test(a);d&&(a=a.replace(La,function(a,b,c){return":"+b+"("+c.replace(/\s/g,"")+")"}),a=Ka(a));var f=Na.test(a);if(f){var g=Ma(a);a=g.A;g=g.matches}a=a.replace(Pa,":host $1");a=a.replace(Qa,function(a,d,f){e||(a=Ra(f,d,b,c),e=e||a.stop,d=a.G,f=a.value);return d+f});f&&(a=Oa(a,g));d&&(a=Ka(a));return a=a.replace(Sa,function(a,b,c,d){return'[dir="'+c+'"] '+b+d+", "+b+'[dir="'+c+'"]'+d})}; -function Ra(a,b,c,e){var d=a.indexOf("::slotted");0<=a.indexOf(":host")?a=Ta(a,e):0!==d&&(a=c?Ua(a,c):a);c=!1;0<=d&&(b="",c=!0);if(c){var f=!0;c&&(a=a.replace(Va,function(a,b){return" > "+b}))}return{value:a,G:b,stop:f}}function Ua(a,b){a=a.split(/(\[.+?\])/);for(var c=[],e=0;e<a.length;e++)if(1===e%2)c.push(a[e]);else{var d=a[e];if(""!==d||e!==a.length-1)d=d.split(":"),d[0]+=b,c.push(d.join(":"))}return c.join("")} -function Ta(a,b){var c=a.match(Wa);return(c=c&&c[2].trim()||"")?c[0].match(Xa)?a.replace(Wa,function(a,c,f){return b+f}):c.split(Xa)[0]===b?c:"should_not_match":a.replace(":host",b)}function Ya(a){":root"===a.selector&&(a.selector="html")}I.prototype.c=function(a){return a.match(":host")?"":a.match("::slotted")?this.b(a,":not(.style-scope)"):Ua(a.trim(),":not(.style-scope)")};aa.Object.defineProperties(I.prototype,{a:{configurable:!0,enumerable:!0,get:function(){return"style-scope"}}}); -var La=/:(nth[-\w]+)\(([^)]+)\)/,Qa=/(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=[])+)/g,Xa=/[[.:#*]/,Pa=/^(::slotted)/,Wa=/(:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/,Va=/(?:::slotted)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/,Sa=/(.*):dir\((?:(ltr|rtl))\)(.*)/,Na=/:(?:matches|any|-(?:webkit|moz)-any)/,K=new I;function N(a,b,c,e,d){this.o=a||null;this.b=b||null;this.w=c||[];this.i=null;this.cssBuild=d||"";this.s=e||"";this.a=this.j=this.m=null}function O(a){return a?a.__styleInfo:null}function Za(a,b){return a.__styleInfo=b}N.prototype.c=function(){return this.o};N.prototype._getStyleRules=N.prototype.c;function $a(a){var b=this.matches||this.matchesSelector||this.mozMatchesSelector||this.msMatchesSelector||this.oMatchesSelector||this.webkitMatchesSelector;return b&&b.call(this,a)}var ab=navigator.userAgent.match("Trident");function bb(){}function cb(a){var b={},c=[],e=0;A(a,function(a){P(a);a.index=e++;a=a.f.cssText;for(var c;c=sa.exec(a);){var d=c[1];":"!==c[2]&&(b[d]=!0)}},function(a){c.push(a)});a.b=c;a=[];for(var d in b)a.push(d);return a} -function P(a){if(!a.f){var b={},c={};R(a,c)&&(b.l=c,a.rules=null);b.cssText=a.parsedCssText.replace(va,"").replace(x,"");a.f=b}}function R(a,b){var c=a.f;if(c){if(c.l)return Object.assign(b,c.l),!0}else{c=a.parsedCssText;for(var e;a=x.exec(c);){e=(a[2]||a[3]).trim();if("inherit"!==e||"unset"!==e)b[a[1].trim()]=e;e=!0}return e}} -function S(a,b,c){b&&(b=0<=b.indexOf(";")?db(a,b,c):Ba(b,function(b,d,f,g){if(!d)return b+g;(d=S(a,c[d],c))&&"initial"!==d?"apply-shim-inherit"===d&&(d="inherit"):d=S(a,c[f]||f,c)||f;return b+(d||"")+g}));return b&&b.trim()||""} -function db(a,b,c){b=b.split(";");for(var e=0,d,f;e<b.length;e++)if(d=b[e]){y.lastIndex=0;if(f=y.exec(d))d=S(a,c[f[1]],c);else if(f=d.indexOf(":"),-1!==f){var g=d.substring(f);g=g.trim();g=S(a,g,c)||g;d=d.substring(0,f)+g}b[e]=d&&d.lastIndexOf(";")===d.length-1?d.slice(0,-1):d||""}return b.join(";")} -function eb(a,b){var c={},e=[];A(a,function(a){a.f||P(a);var d=a.g||a.parsedSelector;b&&a.f.l&&d&&$a.call(b,d)&&(R(a,c),a=a.index,d=parseInt(a/32,10),e[d]=(e[d]||0)|1<<a%32)},null,!0);return{l:c,key:e}} -function fb(a,b,c,e){b.f||P(b);if(b.f.l){var d=G(a);a=d.is;d=d.s;d=a?Ia(a,d):"html";var f=b.parsedSelector,g=":host > *"===f||"html"===f,h=0===f.indexOf(":host")&&!g;"shady"===c&&(g=f===d+" > *."+d||-1!==f.indexOf("html"),h=!g&&0===f.indexOf(d));if(g||h)c=d,h&&(b.g||(b.g=Ja(K,b,K.b,a?"."+a:"",d)),c=b.g||d),e({A:c,K:h,T:g})}}function gb(a,b,c){var e={},d={};A(b,function(b){fb(a,b,c,function(c){$a.call(a._element||a,c.A)&&(c.K?R(b,e):R(b,d))})},null,!0);return{M:d,J:e}} -function hb(a,b,c,e){var d=G(b),f=Ia(d.is,d.s),g=new RegExp("(?:^|[^.#[:])"+(b.extends?"\\"+f.slice(0,-1)+"\\]":f)+"($|[.:[\\s>+~])"),h=O(b);d=h.o;h=h.cssBuild;var l=ib(d,e);return M(b,d,function(b){var d="";b.f||P(b);b.f.cssText&&(d=db(a,b.f.cssText,c));b.cssText=d;if(!r&&!xa(b)&&b.cssText){var h=d=b.cssText;null==b.C&&(b.C=ta.test(d));if(b.C)if(null==b.u){b.u=[];for(var m in l)h=l[m],h=h(d),d!==h&&(d=h,b.u.push(m))}else{for(m=0;m<b.u.length;++m)h=l[b.u[m]],d=h(d);h=d}b.cssText=h;b.g=b.g||b.selector; -d="."+e;m=Ca(b.g);h=0;for(var Ab=m.length,Q=void 0;h<Ab&&(Q=m[h]);h++)m[h]=Q.match(g)?Q.replace(f,d):d+" "+Q;b.selector=m.join(",")}},h)}function ib(a,b){a=a.b;var c={};if(!r&&a)for(var e=0,d=a[e];e<a.length;d=a[++e]){var f=d,g=b;f.h=new RegExp("\\b"+f.keyframesName+"(?!\\B|-)","g");f.a=f.keyframesName+"-"+g;f.g=f.g||f.selector;f.selector=f.g.replace(f.keyframesName,f.a);c[d.keyframesName]=jb(d)}return c}function jb(a){return function(b){return b.replace(a.h,a.a)}} -function kb(a,b){var c=T,e=B(a);a.textContent=z(e,function(a){var d=a.cssText=a.parsedCssText;a.f&&a.f.cssText&&(d=d.replace(ma,"").replace(na,""),a.cssText=db(c,d,b))})}aa.Object.defineProperties(bb.prototype,{a:{configurable:!0,enumerable:!0,get:function(){return"x-scope"}}});var T=new bb;var U={},V=window.customElements;if(V&&!r&&!u){var lb=V.define;V.define=function(a,b,c){U[a]||(U[a]=za(a));lb.call(V,a,b,c)}};function mb(){this.cache={}}mb.prototype.store=function(a,b,c,e){var d=this.cache[a]||[];d.push({l:b,styleElement:c,j:e});100<d.length&&d.shift();this.cache[a]=d};function nb(){}var ob=new RegExp(K.a+"\\s*([^\\s]*)");function pb(a){return(a=(a.classList&&a.classList.value?a.classList.value:a.getAttribute("class")||"").match(ob))?a[1]:""}function qb(a){var b=F(a).getRootNode();return b===a||b===a.ownerDocument?"":(a=b.host)?G(a).is:""} -function rb(a){for(var b=0;b<a.length;b++){var c=a[b];if(c.target!==document.documentElement&&c.target!==document.head)for(var e=0;e<c.addedNodes.length;e++){var d=c.addedNodes[e];if(d.nodeType===Node.ELEMENT_NODE){var f=d.getRootNode(),g=pb(d);if(g&&f===d.ownerDocument&&("style"!==d.localName&&"template"!==d.localName||""===H(d)))Ga(d,g);else if(f instanceof ShadowRoot)for(f=qb(d),f!==g&&Fa(d,g,f),d=window.ShadyDOM.nativeMethods.querySelectorAll.call(d,":not(."+K.a+")"),g=0;g<d.length;g++){f=d[g]; -var h=qb(f);h&&L(f,h)}}}}} -if(!(r||window.ShadyDOM&&window.ShadyDOM.handlesDynamicScoping)){var sb=new MutationObserver(rb),tb=function(a){sb.observe(a,{childList:!0,subtree:!0})};if(window.customElements&&!window.customElements.polyfillWrapFlushCallback)tb(document);else{var ub=function(){tb(document.body)};window.HTMLImports?window.HTMLImports.whenReady(ub):requestAnimationFrame(function(){if("loading"===document.readyState){var a=function(){ub();document.removeEventListener("readystatechange",a)};document.addEventListener("readystatechange", -a)}else ub()})}nb=function(){rb(sb.takeRecords())}}var vb=nb;var W={};var wb=Promise.resolve();function xb(a){if(a=W[a])a._applyShimCurrentVersion=a._applyShimCurrentVersion||0,a._applyShimValidatingVersion=a._applyShimValidatingVersion||0,a._applyShimNextVersion=(a._applyShimNextVersion||0)+1}function yb(a){return a._applyShimCurrentVersion===a._applyShimNextVersion}function zb(a){a._applyShimValidatingVersion=a._applyShimNextVersion;a._validating||(a._validating=!0,wb.then(function(){a._applyShimCurrentVersion=a._applyShimNextVersion;a._validating=!1}))};var Bb={},Cb=new mb;function X(){this.B={};this.c=document.documentElement;var a=new n;a.rules=[];this.h=Za(this.c,new N(a));this.v=!1;this.b=this.a=null}k=X.prototype;k.flush=function(){vb()};k.I=function(a){return B(a)};k.R=function(a){return z(a)};k.prepareTemplate=function(a,b,c){this.prepareTemplateDom(a,b);this.prepareTemplateStyles(a,b,c)}; -k.prepareTemplateStyles=function(a,b,c){if(!a._prepared&&!u){r||U[b]||(U[b]=za(b));a._prepared=!0;a.name=b;a.extends=c;W[b]=a;var e=H(a),d=Da(e);c={is:b,extends:c};for(var f=[],g=a.content.querySelectorAll("style"),h=0;h<g.length;h++){var l=g[h];if(l.hasAttribute("shady-unscoped")){if(!r){var m=l.textContent;wa.has(m)||(wa.add(m),m=l.cloneNode(!0),document.head.appendChild(m));l.parentNode.removeChild(l)}}else f.push(l.textContent),l.parentNode.removeChild(l)}f=f.join("").trim()+(Bb[b]||"");Y(this); -if(!d){if(g=!e)g=y.test(f)||x.test(f),y.lastIndex=0,x.lastIndex=0;h=p(f);g&&v&&this.a&&this.a.transformRules(h,b);a._styleAst=h}g=[];v||(g=cb(a._styleAst));if(!g.length||v)h=r?a.content:null,b=U[b]||null,e=M(c,a._styleAst,null,e,d?f:""),e=e.length?C(e,c.is,h,b):null,a._style=e;a.a=g}};k.L=function(a,b){Bb[b]=a.join(" ")};k.prepareTemplateDom=function(a,b){if(!u){var c=H(a);r||"shady"===c||a._domPrepared||(a._domPrepared=!0,Ea(a.content,b))}}; -function Db(a){var b=G(a),c=b.is;b=b.s;var e=U[c]||null,d=W[c];if(d){c=d._styleAst;var f=d.a;d=H(d);b=new N(c,e,f,b,d);Za(a,b);return b}}function Eb(a){!a.b&&window.ShadyCSS&&window.ShadyCSS.CustomStyleInterface&&(a.b=window.ShadyCSS.CustomStyleInterface,a.b.transformCallback=function(b){a.D(b)},a.b.validateCallback=function(){requestAnimationFrame(function(){(a.b.enqueued||a.v)&&a.flushCustomStyles()})})} -function Y(a){!a.a&&window.ShadyCSS&&window.ShadyCSS.ApplyShim&&(a.a=window.ShadyCSS.ApplyShim,a.a.invalidCallback=xb);Eb(a)} -k.flushCustomStyles=function(){if(!u&&(Y(this),this.b)){var a=this.b.processStyles();if(this.b.enqueued&&!Da(this.h.cssBuild)){if(v){if(!this.h.cssBuild)for(var b=0;b<a.length;b++){var c=this.b.getStyleForCustomStyle(a[b]);if(c&&v&&this.a){var e=B(c);Y(this);this.a.transformRules(e);c.textContent=z(e)}}}else{Fb(this,this.c,this.h);for(b=0;b<a.length;b++)(c=this.b.getStyleForCustomStyle(a[b]))&&kb(c,this.h.m);this.v&&this.styleDocument()}this.b.enqueued=!1}}}; -k.styleElement=function(a,b){if(u){if(b){O(a)||Za(a,new N(null));var c=O(a);c.i=c.i||{};Object.assign(c.i,b);Gb(this,a,c)}}else if(c=O(a)||Db(a))if(a!==this.c&&(this.v=!0),b&&(c.i=c.i||{},Object.assign(c.i,b)),v)Gb(this,a,c);else if(this.flush(),Fb(this,a,c),c.w&&c.w.length){b=G(a).is;var e;a:{if(e=Cb.cache[b])for(var d=e.length-1;0<=d;d--){var f=e[d];b:{var g=c.w;for(var h=0;h<g.length;h++){var l=g[h];if(f.l[l]!==c.m[l]){g=!1;break b}}g=!0}if(g){e=f;break a}}e=void 0}g=e?e.styleElement:null;d=c.j; -(f=e&&e.j)||(f=this.B[b]=(this.B[b]||0)+1,f=b+"-"+f);c.j=f;f=c.j;h=T;h=g?g.textContent||"":hb(h,a,c.m,f);l=O(a);var m=l.a;m&&!r&&m!==g&&(m._useCount--,0>=m._useCount&&m.parentNode&&m.parentNode.removeChild(m));r?l.a?(l.a.textContent=h,g=l.a):h&&(g=C(h,f,a.shadowRoot,l.b)):g?g.parentNode||(ab&&-1<h.indexOf("@media")&&(g.textContent=h),ya(g,null,l.b)):h&&(g=C(h,f,null,l.b));g&&(g._useCount=g._useCount||0,l.a!=g&&g._useCount++,l.a=g);f=g;r||(g=c.j,l=h=a.getAttribute("class")||"",d&&(l=h.replace(new RegExp("\\s*x-scope\\s*"+ -d+"\\s*","g")," ")),l+=(l?" ":"")+"x-scope "+g,h!==l&&E(a,l));e||Cb.store(b,c.m,f,c.j)}}; -function Gb(a,b,c){var e=G(b).is;if(c.i){var d=c.i,f;for(f in d)null===f?b.style.removeProperty(f):b.style.setProperty(f,d[f])}d=W[e];if(!(!d&&b!==a.c||d&&""!==H(d))&&d&&d._style&&!yb(d)){if(yb(d)||d._applyShimValidatingVersion!==d._applyShimNextVersion)Y(a),a.a&&a.a.transformRules(d._styleAst,e),d._style.textContent=M(b,c.o),zb(d);r&&(a=b.shadowRoot)&&(a=a.querySelector("style"))&&(a.textContent=M(b,c.o));c.o=d._styleAst}} -function Hb(a,b){return(b=F(b).getRootNode().host)?O(b)||Db(b)?b:Hb(a,b):a.c}function Fb(a,b,c){var e=Hb(a,b),d=O(e),f=d.m;e===a.c||f||(Fb(a,e,d),f=d.m);a=Object.create(f||null);e=gb(b,c.o,c.cssBuild);b=eb(d.o,b).l;Object.assign(a,e.J,b,e.M);b=c.i;for(var g in b)if((d=b[g])||0===d)a[g]=d;g=T;b=Object.getOwnPropertyNames(a);for(d=0;d<b.length;d++)e=b[d],a[e]=S(g,a[e],a);c.m=a}k.styleDocument=function(a){this.styleSubtree(this.c,a)}; -k.styleSubtree=function(a,b){var c=F(a),e=c.shadowRoot;(e||a===this.c)&&this.styleElement(a,b);if(a=e&&(e.children||e.childNodes))for(c=0;c<a.length;c++)this.styleSubtree(a[c]);else if(c=c.children||c.childNodes)for(a=0;a<c.length;a++)this.styleSubtree(c[a])}; -k.D=function(a){var b=this,c=H(a);c!==this.h.cssBuild&&(this.h.cssBuild=c);if(!Da(c)){var e=B(a);A(e,function(a){if(r)Ya(a);else{var d=K;a.selector=a.parsedSelector;Ya(a);a.selector=a.g=Ja(d,a,d.c,void 0,void 0)}v&&""===c&&(Y(b),b.a&&b.a.transformRule(a))});v?a.textContent=z(e):this.h.o.rules.push(e)}};k.getComputedStyleValue=function(a,b){var c;v||(c=(O(a)||O(Hb(this,a))).m[b]);return(c=c||window.getComputedStyle(a).getPropertyValue(b))?c.trim():""}; -k.P=function(a,b){var c=F(a).getRootNode();b=b?b.split(/\s/):[];c=c.host&&c.host.localName;if(!c){var e=a.getAttribute("class");if(e){e=e.split(/\s/);for(var d=0;d<e.length;d++)if(e[d]===K.a){c=e[d+1];break}}}c&&b.push(K.a,c);v||(c=O(a))&&c.j&&b.push(T.a,c.j);E(a,b.join(" "))};k.F=function(a){return O(a)};k.O=function(a,b){L(a,b)};k.S=function(a,b){L(a,b,!0)};k.N=function(a){return qb(a)};k.H=function(a){return pb(a)};X.prototype.flush=X.prototype.flush;X.prototype.prepareTemplate=X.prototype.prepareTemplate; -X.prototype.styleElement=X.prototype.styleElement;X.prototype.styleDocument=X.prototype.styleDocument;X.prototype.styleSubtree=X.prototype.styleSubtree;X.prototype.getComputedStyleValue=X.prototype.getComputedStyleValue;X.prototype.setElementClass=X.prototype.P;X.prototype._styleInfoForNode=X.prototype.F;X.prototype.transformCustomStyleForDocument=X.prototype.D;X.prototype.getStyleAst=X.prototype.I;X.prototype.styleAstToString=X.prototype.R;X.prototype.flushCustomStyles=X.prototype.flushCustomStyles; -X.prototype.scopeNode=X.prototype.O;X.prototype.unscopeNode=X.prototype.S;X.prototype.scopeForNode=X.prototype.N;X.prototype.currentScopeForNode=X.prototype.H;X.prototype.prepareAdoptedCssText=X.prototype.L;Object.defineProperties(X.prototype,{nativeShadow:{get:function(){return r}},nativeCss:{get:function(){return v}}});var Z=new X,Ib,Jb;window.ShadyCSS&&(Ib=window.ShadyCSS.ApplyShim,Jb=window.ShadyCSS.CustomStyleInterface); -window.ShadyCSS={ScopingShim:Z,prepareTemplate:function(a,b,c){Z.flushCustomStyles();Z.prepareTemplate(a,b,c)},prepareTemplateDom:function(a,b){Z.prepareTemplateDom(a,b)},prepareTemplateStyles:function(a,b,c){Z.flushCustomStyles();Z.prepareTemplateStyles(a,b,c)},styleSubtree:function(a,b){Z.flushCustomStyles();Z.styleSubtree(a,b)},styleElement:function(a){Z.flushCustomStyles();Z.styleElement(a)},styleDocument:function(a){Z.flushCustomStyles();Z.styleDocument(a)},flushCustomStyles:function(){Z.flushCustomStyles()}, -getComputedStyleValue:function(a,b){return Z.getComputedStyleValue(a,b)},nativeCss:v,nativeShadow:r,cssBuild:w,disableRuntime:u};Ib&&(window.ShadyCSS.ApplyShim=Ib);Jb&&(window.ShadyCSS.CustomStyleInterface=Jb);}).call(this); - -//# sourceMappingURL=scoping-shim.min.js.map
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim-utils.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim-utils.js deleted file mode 100644 index c5c27a2..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim-utils.js +++ /dev/null
@@ -1,149 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; -import templateMap from './template-map.js'; -import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars - -/* - * Utilities for handling invalidating apply-shim mixins for a given template. - * - * The invalidation strategy involves keeping track of the "current" version of a template's mixins, and updating that count when a mixin is invalidated. - * The template - */ - -/** @const {string} */ -const CURRENT_VERSION = '_applyShimCurrentVersion'; - -/** @const {string} */ -const NEXT_VERSION = '_applyShimNextVersion'; - -/** @const {string} */ -const VALIDATING_VERSION = '_applyShimValidatingVersion'; - -/** - * @const {Promise<void>} - */ -const promise = Promise.resolve(); - -/** - * @param {string} elementName - */ -export function invalidate(elementName){ - let template = templateMap[elementName]; - if (template) { - invalidateTemplate(template); - } -} - -/** - * This function can be called multiple times to mark a template invalid - * and signal that the style inside must be regenerated. - * - * Use `startValidatingTemplate` to begin an asynchronous validation cycle. - * During that cycle, call `templateIsValidating` to see if the template must - * be revalidated - * @param {HTMLTemplateElement} template - */ -export function invalidateTemplate(template) { - // default the current version to 0 - template[CURRENT_VERSION] = template[CURRENT_VERSION] || 0; - // ensure the "validating for" flag exists - template[VALIDATING_VERSION] = template[VALIDATING_VERSION] || 0; - // increment the next version - template[NEXT_VERSION] = (template[NEXT_VERSION] || 0) + 1; -} - -/** - * @param {string} elementName - * @return {boolean} - */ -export function isValid(elementName) { - let template = templateMap[elementName]; - if (template) { - return templateIsValid(template); - } - return true; -} - -/** - * @param {HTMLTemplateElement} template - * @return {boolean} - */ -export function templateIsValid(template) { - return template[CURRENT_VERSION] === template[NEXT_VERSION]; -} - -/** - * @param {string} elementName - * @return {boolean} - */ -export function isValidating(elementName) { - let template = templateMap[elementName]; - if (template) { - return templateIsValidating(template); - } - return false; -} - -/** - * Returns true if the template is currently invalid and `startValidating` has been called since the last invalidation. - * If false, the template must be validated. - * @param {HTMLTemplateElement} template - * @return {boolean} - */ -export function templateIsValidating(template) { - return !templateIsValid(template) && template[VALIDATING_VERSION] === template[NEXT_VERSION]; -} - -/** - * the template is marked as `validating` for one microtask so that all instances - * found in the tree crawl of `applyStyle` will update themselves, - * but the template will only be updated once. - * @param {string} elementName -*/ -export function startValidating(elementName) { - let template = templateMap[elementName]; - startValidatingTemplate(template); -} - -/** - * Begin an asynchronous invalidation cycle. - * This should be called after every validation of a template - * - * After one microtask, the template will be marked as valid until the next call to `invalidateTemplate` - * @param {HTMLTemplateElement} template - */ -export function startValidatingTemplate(template) { - // remember that the current "next version" is the reason for this validation cycle - template[VALIDATING_VERSION] = template[NEXT_VERSION]; - // however, there only needs to be one async task to clear the counters - if (!template._validating) { - template._validating = true; - promise.then(function() { - // sync the current version to let future invalidations cause a refresh cycle - template[CURRENT_VERSION] = template[NEXT_VERSION]; - template._validating = false; - }); - } -} - -/** - * @return {boolean} - */ -export function elementsAreInvalid() { - for (let elementName in templateMap) { - let template = templateMap[elementName]; - if (!templateIsValid(template)) { - return true; - } - } - return false; -}
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim.js deleted file mode 100644 index e4bc9cd..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim.js +++ /dev/null
@@ -1,525 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -/* - * The apply shim simulates the behavior of `@apply` proposed at - * https://tabatkins.github.io/specs/css-apply-rule/. - * The approach is to convert a property like this: - * - * --foo: {color: red; background: blue;} - * - * to this: - * - * --foo_-_color: red; - * --foo_-_background: blue; - * - * Then where `@apply --foo` is used, that is converted to: - * - * color: var(--foo_-_color); - * background: var(--foo_-_background); - * - * This approach generally works but there are some issues and limitations. - * Consider, for example, that somewhere *between* where `--foo` is set and used, - * another element sets it to: - * - * --foo: { border: 2px solid red; } - * - * We must now ensure that the color and background from the previous setting - * do not apply. This is accomplished by changing the property set to this: - * - * --foo_-_border: 2px solid red; - * --foo_-_color: initial; - * --foo_-_background: initial; - * - * This works but introduces one new issue. - * Consider this setup at the point where the `@apply` is used: - * - * background: orange; - * `@apply` --foo; - * - * In this case the background will be unset (initial) rather than the desired - * `orange`. We address this by altering the property set to use a fallback - * value like this: - * - * color: var(--foo_-_color); - * background: var(--foo_-_background, orange); - * border: var(--foo_-_border); - * - * Note that the default is retained in the property set and the `background` is - * the desired `orange`. This leads us to a limitation. - * - * Limitation 1: - - * Only properties in the rule where the `@apply` - * is used are considered as default values. - * If another rule matches the element and sets `background` with - * less specificity than the rule in which `@apply` appears, - * the `background` will not be set. - * - * Limitation 2: - * - * When using Polymer's `updateStyles` api, new properties may not be set for - * `@apply` properties. - -*/ - -'use strict'; - -import {forEachRule, processVariableAndFallback, rulesForStyle, toCssText, gatherStyleText} from './style-util.js'; -import {MIXIN_MATCH, VAR_ASSIGN} from './common-regex.js'; -import {detectMixin} from './common-utils.js'; -import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars - -const APPLY_NAME_CLEAN = /;\s*/m; -const INITIAL_INHERIT = /^\s*(initial)|(inherit)\s*$/; -const IMPORTANT = /\s*!important/; - -// separator used between mixin-name and mixin-property-name when producing properties -// NOTE: plain '-' may cause collisions in user styles -const MIXIN_VAR_SEP = '_-_'; - -/** - * @typedef {!Object<string, string>} - */ -let PropertyEntry; // eslint-disable-line no-unused-vars - -/** - * @typedef {!Object<string, boolean>} - */ -let DependantsEntry; // eslint-disable-line no-unused-vars - -/** @typedef {{ - * properties: PropertyEntry, - * dependants: DependantsEntry - * }} - */ -let MixinMapEntry; // eslint-disable-line no-unused-vars - -// map of mixin to property names -// --foo: {border: 2px} -> {properties: {(--foo, ['border'])}, dependants: {'element-name': proto}} -class MixinMap { - constructor() { - /** @type {!Object<string, !MixinMapEntry>} */ - this._map = {}; - } - /** - * @param {string} name - * @param {!PropertyEntry} props - */ - set(name, props) { - name = name.trim(); - this._map[name] = { - properties: props, - dependants: {} - } - } - /** - * @param {string} name - * @return {MixinMapEntry} - */ - get(name) { - name = name.trim(); - return this._map[name] || null; - } -} - -/** - * Callback for when an element is marked invalid - * @type {?function(string)} - */ -let invalidCallback = null; - -/** @unrestricted */ -class ApplyShim { - constructor() { - /** @type {?string} */ - this._currentElement = null; - /** @type {HTMLMetaElement} */ - this._measureElement = null; - this._map = new MixinMap(); - } - /** - * return true if `cssText` contains a mixin definition or consumption - * @param {string} cssText - * @return {boolean} - */ - detectMixin(cssText) { - return detectMixin(cssText); - } - - /** - * Gather styles into one style for easier processing - * @param {!HTMLTemplateElement} template - * @return {HTMLStyleElement} - */ - gatherStyles(template) { - const styleText = gatherStyleText(template.content); - if (styleText) { - const style = /** @type {!HTMLStyleElement} */(document.createElement('style')); - style.textContent = styleText; - template.content.insertBefore(style, template.content.firstChild); - return style; - } - return null; - } - /** - * @param {!HTMLTemplateElement} template - * @param {string} elementName - * @return {StyleNode} - */ - transformTemplate(template, elementName) { - if (template._gatheredStyle === undefined) { - template._gatheredStyle = this.gatherStyles(template); - } - /** @type {HTMLStyleElement} */ - const style = template._gatheredStyle; - return style ? this.transformStyle(style, elementName) : null; - } - /** - * @param {!HTMLStyleElement} style - * @param {string} elementName - * @return {StyleNode} - */ - transformStyle(style, elementName = '') { - let ast = rulesForStyle(style); - this.transformRules(ast, elementName); - style.textContent = toCssText(ast); - return ast; - } - /** - * @param {!HTMLStyleElement} style - * @return {StyleNode} - */ - transformCustomStyle(style) { - let ast = rulesForStyle(style); - forEachRule(ast, (rule) => { - if (rule['selector'] === ':root') { - rule['selector'] = 'html'; - } - this.transformRule(rule); - }) - style.textContent = toCssText(ast); - return ast; - } - /** - * @param {StyleNode} rules - * @param {string} elementName - */ - transformRules(rules, elementName) { - this._currentElement = elementName; - forEachRule(rules, (r) => { - this.transformRule(r); - }); - this._currentElement = null; - } - /** - * @param {!StyleNode} rule - */ - transformRule(rule) { - rule['cssText'] = this.transformCssText(rule['parsedCssText'], rule); - // :root was only used for variable assignment in property shim, - // but generates invalid selectors with real properties. - // replace with `:host > *`, which serves the same effect - if (rule['selector'] === ':root') { - rule['selector'] = ':host > *'; - } - } - /** - * @param {string} cssText - * @param {!StyleNode} rule - * @return {string} - */ - transformCssText(cssText, rule) { - // produce variables - cssText = cssText.replace(VAR_ASSIGN, (matchText, propertyName, valueProperty, valueMixin) => - this._produceCssProperties(matchText, propertyName, valueProperty, valueMixin, rule)); - // consume mixins - return this._consumeCssProperties(cssText, rule); - } - /** - * @param {string} property - * @return {string} - */ - _getInitialValueForProperty(property) { - if (!this._measureElement) { - this._measureElement = /** @type {HTMLMetaElement} */(document.createElement('meta')); - this._measureElement.setAttribute('apply-shim-measure', ''); - this._measureElement.style.all = 'initial'; - document.head.appendChild(this._measureElement); - } - return window.getComputedStyle(this._measureElement).getPropertyValue(property); - } - /** - * Walk over all rules before this rule to find fallbacks for mixins - * - * @param {!StyleNode} startRule - * @return {!Object} - */ - _fallbacksFromPreviousRules(startRule) { - // find the "top" rule - let topRule = startRule; - while (topRule['parent']) { - topRule = topRule['parent']; - } - const fallbacks = {}; - let seenStartRule = false; - forEachRule(topRule, (r) => { - // stop when we hit the input rule - seenStartRule = seenStartRule || r === startRule; - if (seenStartRule) { - return; - } - // NOTE: Only matching selectors are "safe" for this fallback processing - // It would be prohibitive to run `matchesSelector()` on each selector, - // so we cheat and only check if the same selector string is used, which - // guarantees things like specificity matching - if (r['selector'] === startRule['selector']) { - Object.assign(fallbacks, this._cssTextToMap(r['parsedCssText'])); - } - }); - return fallbacks; - } - /** - * replace mixin consumption with variable consumption - * @param {string} text - * @param {!StyleNode=} rule - * @return {string} - */ - _consumeCssProperties(text, rule) { - /** @type {Array} */ - let m = null; - // loop over text until all mixins with defintions have been applied - while((m = MIXIN_MATCH.exec(text))) { - let matchText = m[0]; - let mixinName = m[1]; - let idx = m.index; - // collect properties before apply to be "defaults" if mixin might override them - // match includes a "prefix", so find the start and end positions of @apply - let applyPos = idx + matchText.indexOf('@apply'); - let afterApplyPos = idx + matchText.length; - // find props defined before this @apply - let textBeforeApply = text.slice(0, applyPos); - let textAfterApply = text.slice(afterApplyPos); - let defaults = rule ? this._fallbacksFromPreviousRules(rule) : {}; - Object.assign(defaults, this._cssTextToMap(textBeforeApply)); - let replacement = this._atApplyToCssProperties(mixinName, defaults); - // use regex match position to replace mixin, keep linear processing time - text = `${textBeforeApply}${replacement}${textAfterApply}`; - // move regex search to _after_ replacement - MIXIN_MATCH.lastIndex = idx + replacement.length; - } - return text; - } - /** - * produce variable consumption at the site of mixin consumption - * `@apply` --foo; -> for all props (${propname}: var(--foo_-_${propname}, ${fallback[propname]}})) - * Example: - * border: var(--foo_-_border); padding: var(--foo_-_padding, 2px) - * - * @param {string} mixinName - * @param {Object} fallbacks - * @return {string} - */ - _atApplyToCssProperties(mixinName, fallbacks) { - mixinName = mixinName.replace(APPLY_NAME_CLEAN, ''); - let vars = []; - let mixinEntry = this._map.get(mixinName); - // if we depend on a mixin before it is created - // make a sentinel entry in the map to add this element as a dependency for when it is defined. - if (!mixinEntry) { - this._map.set(mixinName, {}); - mixinEntry = this._map.get(mixinName); - } - if (mixinEntry) { - if (this._currentElement) { - mixinEntry.dependants[this._currentElement] = true; - } - let p, parts, f; - const properties = mixinEntry.properties; - for (p in properties) { - f = fallbacks && fallbacks[p]; - parts = [p, ': var(', mixinName, MIXIN_VAR_SEP, p]; - if (f) { - parts.push(',', f.replace(IMPORTANT, '')); - } - parts.push(')'); - if (IMPORTANT.test(properties[p])) { - parts.push(' !important'); - } - vars.push(parts.join('')); - } - } - return vars.join('; '); - } - - /** - * @param {string} property - * @param {string} value - * @return {string} - */ - _replaceInitialOrInherit(property, value) { - let match = INITIAL_INHERIT.exec(value); - if (match) { - if (match[1]) { - // initial - // replace `initial` with the concrete initial value for this property - value = this._getInitialValueForProperty(property); - } else { - // inherit - // with this purposfully illegal value, the variable will be invalid at - // compute time (https://www.w3.org/TR/css-variables/#invalid-at-computed-value-time) - // and for inheriting values, will behave similarly - // we cannot support the same behavior for non inheriting values like 'border' - value = 'apply-shim-inherit'; - } - } - return value; - } - - /** - * "parse" a mixin definition into a map of properties and values - * cssTextToMap('border: 2px solid black') -> ('border', '2px solid black') - * @param {string} text - * @param {boolean=} replaceInitialOrInherit - * @return {!Object<string, string>} - */ - _cssTextToMap(text, replaceInitialOrInherit = false) { - let props = text.split(';'); - let property, value; - let out = {}; - for (let i = 0, p, sp; i < props.length; i++) { - p = props[i]; - if (p) { - sp = p.split(':'); - // ignore lines that aren't definitions like @media - if (sp.length > 1) { - property = sp[0].trim(); - // some properties may have ':' in the value, like data urls - value = sp.slice(1).join(':'); - if (replaceInitialOrInherit) { - value = this._replaceInitialOrInherit(property, value); - } - out[property] = value; - } - } - } - return out; - } - - /** - * @param {MixinMapEntry} mixinEntry - */ - _invalidateMixinEntry(mixinEntry) { - if (!invalidCallback) { - return; - } - for (let elementName in mixinEntry.dependants) { - if (elementName !== this._currentElement) { - invalidCallback(elementName); - } - } - } - - /** - * @param {string} matchText - * @param {string} propertyName - * @param {?string} valueProperty - * @param {?string} valueMixin - * @param {!StyleNode} rule - * @return {string} - */ - _produceCssProperties(matchText, propertyName, valueProperty, valueMixin, rule) { - // handle case where property value is a mixin - if (valueProperty) { - // form: --mixin2: var(--mixin1), where --mixin1 is in the map - processVariableAndFallback(valueProperty, (prefix, value) => { - if (value && this._map.get(value)) { - valueMixin = `@apply ${value};` - } - }); - } - if (!valueMixin) { - return matchText; - } - let mixinAsProperties = this._consumeCssProperties('' + valueMixin, rule); - let prefix = matchText.slice(0, matchText.indexOf('--')); - // `initial` and `inherit` as properties in a map should be replaced because - // these keywords are eagerly evaluated when the mixin becomes CSS Custom Properties, - // and would set the variable value, rather than carry the keyword to the `var()` usage. - let mixinValues = this._cssTextToMap(mixinAsProperties, true); - let combinedProps = mixinValues; - let mixinEntry = this._map.get(propertyName); - let oldProps = mixinEntry && mixinEntry.properties; - if (oldProps) { - // NOTE: since we use mixin, the map of properties is updated here - // and this is what we want. - combinedProps = Object.assign(Object.create(oldProps), mixinValues); - } else { - this._map.set(propertyName, combinedProps); - } - let out = []; - let p, v; - // set variables defined by current mixin - let needToInvalidate = false; - for (p in combinedProps) { - v = mixinValues[p]; - // if property not defined by current mixin, set initial - if (v === undefined) { - v = 'initial'; - } - if (oldProps && !(p in oldProps)) { - needToInvalidate = true; - } - out.push(`${propertyName}${MIXIN_VAR_SEP}${p}: ${v}`); - } - if (needToInvalidate) { - this._invalidateMixinEntry(mixinEntry); - } - if (mixinEntry) { - mixinEntry.properties = combinedProps; - } - // because the mixinMap is global, the mixin might conflict with - // a different scope's simple variable definition: - // Example: - // some style somewhere: - // --mixin1:{ ... } - // --mixin2: var(--mixin1); - // some other element: - // --mixin1: 10px solid red; - // --foo: var(--mixin1); - // In this case, we leave the original variable definition in place. - if (valueProperty) { - prefix = `${matchText};${prefix}`; - } - return `${prefix}${out.join('; ')};`; - } -} - -/* exports */ -/* eslint-disable no-self-assign */ -ApplyShim.prototype['detectMixin'] = ApplyShim.prototype.detectMixin; -ApplyShim.prototype['transformStyle'] = ApplyShim.prototype.transformStyle; -ApplyShim.prototype['transformCustomStyle'] = ApplyShim.prototype.transformCustomStyle; -ApplyShim.prototype['transformRules'] = ApplyShim.prototype.transformRules; -ApplyShim.prototype['transformRule'] = ApplyShim.prototype.transformRule; -ApplyShim.prototype['transformTemplate'] = ApplyShim.prototype.transformTemplate; -ApplyShim.prototype['_separator'] = MIXIN_VAR_SEP; -/* eslint-enable no-self-assign */ -Object.defineProperty(ApplyShim.prototype, 'invalidCallback', { - /** @return {?function(string)} */ - get() { - return invalidCallback; - }, - /** @param {?function(string)} cb */ - set(cb) { - invalidCallback = cb; - } -}); - -export default ApplyShim;
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/common-regex.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/common-regex.js deleted file mode 100644 index 126fc15c..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/common-regex.js +++ /dev/null
@@ -1,19 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -export const VAR_ASSIGN = /(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};{])+)|\{([^}]*)\}(?:(?=[;\s}])|$))/gi; -export const MIXIN_MATCH = /(?:^|\W+)@apply\s*\(?([^);\n]*)\)?/gi; -export const VAR_CONSUMED = /(--[\w-]+)\s*([:,;)]|$)/gi; -export const ANIMATION_MATCH = /(animation\s*:)|(animation-name\s*:)/; -export const MEDIA_MATCH = /@media\s(.*)/; -export const IS_VAR = /^--/; -export const BRACKETED = /\{[^}]*\}/g; -export const HOST_PREFIX = '(?:^|[^.#[:])'; -export const HOST_SUFFIX = '($|[.:[\\s>+~])';
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/common-utils.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/common-utils.js deleted file mode 100644 index 863250b..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/common-utils.js +++ /dev/null
@@ -1,59 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -import { MIXIN_MATCH, VAR_ASSIGN } from './common-regex.js'; - -/** - * @param {Element} element - * @param {Object=} properties - */ -export function updateNativeProperties(element, properties) { - // remove previous properties - for (let p in properties) { - // NOTE: for bc with shim, don't apply null values. - if (p === null) { - element.style.removeProperty(p); - } else { - element.style.setProperty(p, properties[p]); - } - } -} - -/** - * @param {Element} element - * @param {string} property - * @return {string} - */ -export function getComputedStyleValue(element, property) { - /** - * @const {string} - */ - const value = window.getComputedStyle(element).getPropertyValue(property); - if (!value) { - return ''; - } else { - return value.trim(); - } -} - -/** - * return true if `cssText` contains a mixin definition or consumption - * @param {string} cssText - * @return {boolean} - */ -export function detectMixin(cssText) { - const has = MIXIN_MATCH.test(cssText) || VAR_ASSIGN.test(cssText); - // reset state of the regexes - MIXIN_MATCH.lastIndex = 0; - VAR_ASSIGN.lastIndex = 0; - return has; -}
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/css-parse.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/css-parse.js deleted file mode 100644 index 8a8fb1c..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/css-parse.js +++ /dev/null
@@ -1,264 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -/* -Extremely simple css parser. Intended to be not more than what we need -and definitely not necessarily correct =). -*/ - -'use strict'; - -/** @unrestricted */ -class StyleNode { - constructor() { - /** @type {number} */ - this['start'] = 0; - /** @type {number} */ - this['end'] = 0; - /** @type {StyleNode} */ - this['previous'] = null; - /** @type {StyleNode} */ - this['parent'] = null; - /** @type {Array<StyleNode>} */ - this['rules'] = null; - /** @type {string} */ - this['parsedCssText'] = ''; - /** @type {string} */ - this['cssText'] = ''; - /** @type {boolean} */ - this['atRule'] = false; - /** @type {number} */ - this['type'] = 0; - /** @type {string} */ - this['keyframesName'] = ''; - /** @type {string} */ - this['selector'] = ''; - /** @type {string} */ - this['parsedSelector'] = ''; - } -} - -export {StyleNode} - -// given a string of css, return a simple rule tree -/** - * @param {string} text - * @return {StyleNode} - */ -export function parse(text) { - text = clean(text); - return parseCss(lex(text), text); -} - -// remove stuff we don't care about that may hinder parsing -/** - * @param {string} cssText - * @return {string} - */ -function clean(cssText) { - return cssText.replace(RX.comments, '').replace(RX.port, ''); -} - -// super simple {...} lexer that returns a node tree -/** - * @param {string} text - * @return {StyleNode} - */ -function lex(text) { - let root = new StyleNode(); - root['start'] = 0; - root['end'] = text.length - let n = root; - for (let i = 0, l = text.length; i < l; i++) { - if (text[i] === OPEN_BRACE) { - if (!n['rules']) { - n['rules'] = []; - } - let p = n; - let previous = p['rules'][p['rules'].length - 1] || null; - n = new StyleNode(); - n['start'] = i + 1; - n['parent'] = p; - n['previous'] = previous; - p['rules'].push(n); - } else if (text[i] === CLOSE_BRACE) { - n['end'] = i + 1; - n = n['parent'] || root; - } - } - return root; -} - -// add selectors/cssText to node tree -/** - * @param {StyleNode} node - * @param {string} text - * @return {StyleNode} - */ -function parseCss(node, text) { - let t = text.substring(node['start'], node['end'] - 1); - node['parsedCssText'] = node['cssText'] = t.trim(); - if (node['parent']) { - let ss = node['previous'] ? node['previous']['end'] : node['parent']['start']; - t = text.substring(ss, node['start'] - 1); - t = _expandUnicodeEscapes(t); - t = t.replace(RX.multipleSpaces, ' '); - // TODO(sorvell): ad hoc; make selector include only after last ; - // helps with mixin syntax - t = t.substring(t.lastIndexOf(';') + 1); - let s = node['parsedSelector'] = node['selector'] = t.trim(); - node['atRule'] = (s.indexOf(AT_START) === 0); - // note, support a subset of rule types... - if (node['atRule']) { - if (s.indexOf(MEDIA_START) === 0) { - node['type'] = types.MEDIA_RULE; - } else if (s.match(RX.keyframesRule)) { - node['type'] = types.KEYFRAMES_RULE; - node['keyframesName'] = - node['selector'].split(RX.multipleSpaces).pop(); - } - } else { - if (s.indexOf(VAR_START) === 0) { - node['type'] = types.MIXIN_RULE; - } else { - node['type'] = types.STYLE_RULE; - } - } - } - let r$ = node['rules']; - if (r$) { - for (let i = 0, l = r$.length, r; - (i < l) && (r = r$[i]); i++) { - parseCss(r, text); - } - } - return node; -} - -/** - * conversion of sort unicode escapes with spaces like `\33 ` (and longer) into - * expanded form that doesn't require trailing space `\000033` - * @param {string} s - * @return {string} - */ -function _expandUnicodeEscapes(s) { - return s.replace(/\\([0-9a-f]{1,6})\s/gi, function() { - let code = arguments[1], - repeat = 6 - code.length; - while (repeat--) { - code = '0' + code; - } - return '\\' + code; - }); -} - -/** - * stringify parsed css. - * @param {StyleNode} node - * @param {boolean=} preserveProperties - * @param {string=} text - * @return {string} - */ -export function stringify(node, preserveProperties, text = '') { - // calc rule cssText - let cssText = ''; - if (node['cssText'] || node['rules']) { - let r$ = node['rules']; - if (r$ && !_hasMixinRules(r$)) { - for (let i = 0, l = r$.length, r; - (i < l) && (r = r$[i]); i++) { - cssText = stringify(r, preserveProperties, cssText); - } - } else { - cssText = preserveProperties ? node['cssText'] : - removeCustomProps(node['cssText']); - cssText = cssText.trim(); - if (cssText) { - cssText = ' ' + cssText + '\n'; - } - } - } - // emit rule if there is cssText - if (cssText) { - if (node['selector']) { - text += node['selector'] + ' ' + OPEN_BRACE + '\n'; - } - text += cssText; - if (node['selector']) { - text += CLOSE_BRACE + '\n\n'; - } - } - return text; -} - -/** - * @param {Array<StyleNode>} rules - * @return {boolean} - */ -function _hasMixinRules(rules) { - let r = rules[0]; - return Boolean(r) && Boolean(r['selector']) && r['selector'].indexOf(VAR_START) === 0; -} - -/** - * @param {string} cssText - * @return {string} - */ -function removeCustomProps(cssText) { - cssText = removeCustomPropAssignment(cssText); - return removeCustomPropApply(cssText); -} - -/** - * @param {string} cssText - * @return {string} - */ -export function removeCustomPropAssignment(cssText) { - return cssText - .replace(RX.customProp, '') - .replace(RX.mixinProp, ''); -} - -/** - * @param {string} cssText - * @return {string} - */ -function removeCustomPropApply(cssText) { - return cssText - .replace(RX.mixinApply, '') - .replace(RX.varApply, ''); -} - -/** @enum {number} */ -export const types = { - STYLE_RULE: 1, - KEYFRAMES_RULE: 7, - MEDIA_RULE: 4, - MIXIN_RULE: 1000 -} - -const OPEN_BRACE = '{'; -const CLOSE_BRACE = '}'; - -// helper regexp's -const RX = { - comments: /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim, - port: /@import[^;]*;/gim, - customProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?(?:[;\n]|$)/gim, - mixinProp: /(?:^[^;\-\s}]+)?--[^;{}]*?:[^{};]*?{[^}]*?}(?:[;\n]|$)?/gim, - mixinApply: /@apply\s*\(?[^);]*\)?\s*(?:[;\n]|$)?/gim, - varApply: /[^;:]*?:[^;]*?var\([^;]*\)(?:[;\n]|$)?/gim, - keyframesRule: /^@[^\s]*keyframes/, - multipleSpaces: /\s+/g -} - -const VAR_START = '--'; -const MEDIA_START = '@media'; -const AT_START = '@';
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/custom-style-interface.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/custom-style-interface.js deleted file mode 100644 index 257433b..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/custom-style-interface.js +++ /dev/null
@@ -1,164 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -import documentWait from './document-wait.js'; - -/** - * @typedef {HTMLStyleElement | {getStyle: function():HTMLStyleElement}} - */ -export let CustomStyleProvider; - -const SEEN_MARKER = '__seenByShadyCSS'; -const CACHED_STYLE = '__shadyCSSCachedStyle'; - -/** @type {?function(!HTMLStyleElement)} */ -let transformFn = null; - -/** @type {?function()} */ -let validateFn = null; - -/** -This interface is provided to add document-level <style> elements to ShadyCSS for processing. -These styles must be processed by ShadyCSS to simulate ShadowRoot upper-bound encapsulation from outside styles -In addition, these styles may also need to be processed for @apply rules and CSS Custom Properties - -To add document-level styles to ShadyCSS, one can call `ShadyCSS.addDocumentStyle(styleElement)` or `ShadyCSS.addDocumentStyle({getStyle: () => styleElement})` - -In addition, if the process used to discover document-level styles can be synchronously flushed, one should set `ShadyCSS.documentStyleFlush`. -This function will be called when calculating styles. - -An example usage of the document-level styling api can be found in `examples/document-style-lib.js` - -@unrestricted -*/ -export default class CustomStyleInterface { - constructor() { - /** @type {!Array<!CustomStyleProvider>} */ - this['customStyles'] = []; - this['enqueued'] = false; - // NOTE(dfreedm): use quotes here to prevent closure inlining to `function(){}`; - documentWait(() => { - if (window['ShadyCSS']['flushCustomStyles']) { - window['ShadyCSS']['flushCustomStyles'](); - } - }) - } - /** - * Queue a validation for new custom styles to batch style recalculations - */ - enqueueDocumentValidation() { - if (this['enqueued'] || !validateFn) { - return; - } - this['enqueued'] = true; - documentWait(validateFn); - } - /** - * @param {!HTMLStyleElement} style - */ - addCustomStyle(style) { - if (!style[SEEN_MARKER]) { - style[SEEN_MARKER] = true; - this['customStyles'].push(style); - this.enqueueDocumentValidation(); - } - } - /** - * @param {!CustomStyleProvider} customStyle - * @return {HTMLStyleElement} - */ - getStyleForCustomStyle(customStyle) { - if (customStyle[CACHED_STYLE]) { - return customStyle[CACHED_STYLE]; - } - let style; - if (customStyle['getStyle']) { - style = customStyle['getStyle'](); - } else { - style = customStyle; - } - return style; - } - /** - * @return {!Array<!CustomStyleProvider>} - */ - processStyles() { - const cs = this['customStyles']; - for (let i = 0; i < cs.length; i++) { - const customStyle = cs[i]; - if (customStyle[CACHED_STYLE]) { - continue; - } - const style = this.getStyleForCustomStyle(customStyle); - if (style) { - // HTMLImports polyfill may have cloned the style into the main document, - // which is referenced with __appliedElement. - const styleToTransform = /** @type {!HTMLStyleElement} */(style['__appliedElement'] || style); - if (transformFn) { - transformFn(styleToTransform); - } - customStyle[CACHED_STYLE] = styleToTransform; - } - } - return cs; - } -} - -/* eslint-disable no-self-assign */ -CustomStyleInterface.prototype['addCustomStyle'] = CustomStyleInterface.prototype.addCustomStyle; -CustomStyleInterface.prototype['getStyleForCustomStyle'] = CustomStyleInterface.prototype.getStyleForCustomStyle; -CustomStyleInterface.prototype['processStyles'] = CustomStyleInterface.prototype.processStyles; -/* eslint-enable no-self-assign */ - -Object.defineProperties(CustomStyleInterface.prototype, { - 'transformCallback': { - /** @return {?function(!HTMLStyleElement)} */ - get() { - return transformFn; - }, - /** @param {?function(!HTMLStyleElement)} fn */ - set(fn) { - transformFn = fn; - } - }, - 'validateCallback': { - /** @return {?function()} */ - get() { - return validateFn; - }, - /** - * @param {?function()} fn - * @this {CustomStyleInterface} - */ - set(fn) { - let needsEnqueue = false; - if (!validateFn) { - needsEnqueue = true; - } - validateFn = fn; - if (needsEnqueue) { - this.enqueueDocumentValidation(); - } - }, - } -}) - -/** @typedef {{ - * customStyles: !Array<!CustomStyleProvider>, - * addCustomStyle: function(!CustomStyleProvider), - * getStyleForCustomStyle: function(!CustomStyleProvider): HTMLStyleElement, - * findStyles: function(), - * transformCallback: ?function(!HTMLStyleElement), - * validateCallback: ?function() - * }} - */ -export const CustomStyleInterfaceInterface = {};
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/document-wait.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/document-wait.js deleted file mode 100644 index 398ca05..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/document-wait.js +++ /dev/null
@@ -1,45 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -/** @type {Promise<void>} */ -let readyPromise = null; - -/** @type {?function(?function())} */ -let whenReady = window['HTMLImports'] && window['HTMLImports']['whenReady'] || null; - -/** @type {function()} */ -let resolveFn; - -/** - * @param {?function()} callback - */ -export default function documentWait(callback) { - requestAnimationFrame(function() { - if (whenReady) { - whenReady(callback) - } else { - if (!readyPromise) { - readyPromise = new Promise((resolve) => {resolveFn = resolve}); - if (document.readyState === 'complete') { - resolveFn(); - } else { - document.addEventListener('readystatechange', () => { - if (document.readyState === 'complete') { - resolveFn(); - } - }); - } - } - readyPromise.then(function(){ callback && callback(); }); - } - }); -}
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/document-watcher.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/document-watcher.js deleted file mode 100644 index 9cf34f05..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/document-watcher.js +++ /dev/null
@@ -1,198 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -import {nativeShadow} from './style-settings.js'; -import StyleTransformer from './style-transformer.js'; -import {getIsExtends, elementHasBuiltCss, wrap} from './style-util.js'; - -export let flush = function() {}; - -/** - * @param {!Element} element - * @return {string} - */ -function getClasses(element) { - if (element.classList && element.classList.value) { - return element.classList.value; - } else { - // NOTE: className is patched to remove scoping classes in ShadyDOM - // use getAttribute('class') instead, which is unpatched - return element.getAttribute('class') || ''; - } -} - -const scopeRegExp = new RegExp(`${StyleTransformer.SCOPE_NAME}\\s*([^\\s]*)`); - -/** - * @param {!Element} element - * @return {string} - */ -export function getCurrentScope(element) { - const match = getClasses(element).match(scopeRegExp); - if (match) { - return match[1]; - } else { - return ''; - } -} - -/** - * @param {!Node} node - */ -export function getOwnerScope(node) { - const ownerRoot = wrap(node).getRootNode(); - if (ownerRoot === node || ownerRoot === node.ownerDocument) { - return ''; - } - const host = /** @type {!ShadowRoot} */(ownerRoot).host; - if (!host) { - // this may actually be a document fragment - return ''; - } - return getIsExtends(host).is; -} - -/** - * @param {!Element} element - */ -export function ensureCorrectScope(element) { - const currentScope = getCurrentScope(element); - const ownerRoot = wrap(element).getRootNode(); - if (ownerRoot === element) { - return; - } - if (currentScope && ownerRoot === element.ownerDocument) { - // node was scoped, but now is in document - StyleTransformer.domRemoveScope(element, currentScope); - } else if (ownerRoot instanceof ShadowRoot) { - const ownerScope = getOwnerScope(element); - if (ownerScope !== currentScope) { - // node was scoped, but not by its current owner - StyleTransformer.domReplaceScope(element, currentScope, ownerScope); - } - } -} - -/** - * @param {!HTMLElement|!HTMLDocument} element - */ -export function ensureCorrectSubtreeScoping(element) { - // find unscoped subtree nodes - const unscopedNodes = window['ShadyDOM']['nativeMethods']['querySelectorAll'].call( - element, `:not(.${StyleTransformer.SCOPE_NAME})`); - - for (let j = 0; j < unscopedNodes.length; j++) { - // it's possible, during large batch inserts, that nodes that aren't - // scoped within the current scope were added. - // To make sure that any unscoped nodes that were inserted in the current batch are correctly styled, - // query all unscoped nodes and force their style-scope to be applied. - // This could happen if a sub-element appended an unscoped node in its shadowroot and this function - // runs on a parent element of the host of that unscoped node: - // parent-element -> element -> unscoped node - // Here unscoped node should have the style-scope element, not parent-element. - const unscopedNode = unscopedNodes[j]; - const scopeForPreviouslyUnscopedNode = getOwnerScope(unscopedNode); - if (scopeForPreviouslyUnscopedNode) { - StyleTransformer.element(unscopedNode, scopeForPreviouslyUnscopedNode); - } - } -} - -/** - * @param {HTMLElement} el - * @return {boolean} - */ -function isElementWithBuiltCss(el) { - if (el.localName === 'style' || el.localName === 'template') { - return elementHasBuiltCss(el); - } - return false; -} - -/** - * @param {Array<MutationRecord|null>|null} mxns - */ -function handler(mxns) { - for (let x=0; x < mxns.length; x++) { - let mxn = mxns[x]; - if (mxn.target === document.documentElement || - mxn.target === document.head) { - continue; - } - for (let i=0; i < mxn.addedNodes.length; i++) { - let n = mxn.addedNodes[i]; - if (n.nodeType !== Node.ELEMENT_NODE) { - continue; - } - n = /** @type {HTMLElement} */(n); // eslint-disable-line no-self-assign - let root = n.getRootNode(); - let currentScope = getCurrentScope(n); - // node was scoped, but now is in document - // If this element has built css, we must not remove scoping as this node - // will be used as a template or style without re - applying scoping as an optimization - if (currentScope && root === n.ownerDocument && !isElementWithBuiltCss(n)) { - StyleTransformer.domRemoveScope(n, currentScope); - } else if (root instanceof ShadowRoot) { - const newScope = getOwnerScope(n); - // rescope current node and subtree if necessary - if (newScope !== currentScope) { - StyleTransformer.domReplaceScope(n, currentScope, newScope); - } - // make sure all the subtree elements are scoped correctly - ensureCorrectSubtreeScoping(n); - } - } - } -} - -// if native Shadow DOM is being used, or ShadyDOM handles dynamic scoiping, do not activate the MutationObserver -if (!nativeShadow && !(window['ShadyDOM'] && window['ShadyDOM']['handlesDynamicScoping'])) { - let observer = new MutationObserver(handler); - let start = (node) => { - observer.observe(node, {childList: true, subtree: true}); - } - let nativeCustomElements = (window['customElements'] && - !window['customElements']['polyfillWrapFlushCallback']); - // need to start immediately with native custom elements - // TODO(dfreedm): with polyfilled HTMLImports and native custom elements - // excessive mutations may be observed; this can be optimized via cooperation - // with the HTMLImports polyfill. - if (nativeCustomElements) { - start(document); - } else { - let delayedStart = () => { - start(document.body); - } - // use polyfill timing if it's available - if (window['HTMLImports']) { - window['HTMLImports']['whenReady'](delayedStart); - // otherwise push beyond native imports being ready - // which requires RAF + readystate interactive. - } else { - requestAnimationFrame(function() { - if (document.readyState === 'loading') { - let listener = function() { - delayedStart(); - document.removeEventListener('readystatechange', listener); - } - document.addEventListener('readystatechange', listener); - } else { - delayedStart(); - } - }); - } - } - - flush = function() { - handler(observer.takeRecords()); - } -}
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/scoping-shim.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/scoping-shim.js deleted file mode 100644 index 4deeece..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/scoping-shim.js +++ /dev/null
@@ -1,607 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -import {parse, StyleNode} from './css-parse.js'; -import {nativeShadow, nativeCssVariables, disableRuntime} from './style-settings.js'; -import StyleTransformer from './style-transformer.js'; -import * as StyleUtil from './style-util.js'; -import StyleProperties from './style-properties.js'; -import {ensureStylePlaceholder, getStylePlaceholder} from './style-placeholder.js'; -import StyleInfo from './style-info.js'; -import StyleCache from './style-cache.js'; -import {flush as watcherFlush, getOwnerScope, getCurrentScope} from './document-watcher.js'; -import templateMap from './template-map.js'; -import * as ApplyShimUtils from './apply-shim-utils.js'; -import {updateNativeProperties, detectMixin} from './common-utils.js'; -import {CustomStyleInterfaceInterface} from './custom-style-interface.js'; // eslint-disable-line no-unused-vars - -/** @type {!Object<string, string>} */ -const adoptedCssTextMap = {}; - -/** - * @const {StyleCache} - */ -const styleCache = new StyleCache(); - -export default class ScopingShim { - constructor() { - this._scopeCounter = {}; - this._documentOwner = /** @type {!HTMLElement} */(document.documentElement); - let ast = new StyleNode(); - ast['rules'] = []; - this._documentOwnerStyleInfo = StyleInfo.set(this._documentOwner, new StyleInfo(ast)); - this._elementsHaveApplied = false; - /** @type {?Object} */ - this._applyShim = null; - /** @type {?CustomStyleInterfaceInterface} */ - this._customStyleInterface = null; - } - flush() { - watcherFlush(); - } - _generateScopeSelector(name) { - let id = this._scopeCounter[name] = (this._scopeCounter[name] || 0) + 1; - return `${name}-${id}`; - } - getStyleAst(style) { - return StyleUtil.rulesForStyle(style); - } - styleAstToString(ast) { - return StyleUtil.toCssText(ast); - } - _gatherStyles(template) { - return StyleUtil.gatherStyleText(template.content); - } - /** - * Prepare the styling and template for the given element type - * - * @param {!HTMLTemplateElement} template - * @param {string} elementName - * @param {string=} typeExtension - */ - prepareTemplate(template, elementName, typeExtension) { - this.prepareTemplateDom(template, elementName); - this.prepareTemplateStyles(template, elementName, typeExtension); - } - /** - * Prepare styling for the given element type - * @param {!HTMLTemplateElement} template - * @param {string} elementName - * @param {string=} typeExtension - */ - prepareTemplateStyles(template, elementName, typeExtension) { - if (template._prepared || disableRuntime) { - return; - } - // style placeholders are only used when ShadyDOM is active - if (!nativeShadow) { - ensureStylePlaceholder(elementName); - } - template._prepared = true; - template.name = elementName; - template.extends = typeExtension; - templateMap[elementName] = template; - let cssBuild = StyleUtil.getCssBuild(template); - const optimalBuild = StyleUtil.isOptimalCssBuild(cssBuild); - let info = { - is: elementName, - extends: typeExtension, - }; - let cssText = this._gatherStyles(template) + (adoptedCssTextMap[elementName] || ''); - // check if the styling has mixin definitions or uses - this._ensure(); - if (!optimalBuild) { - let hasMixins = !cssBuild && detectMixin(cssText); - let ast = parse(cssText); - // only run the applyshim transforms if there is a mixin involved - if (hasMixins && nativeCssVariables && this._applyShim) { - this._applyShim['transformRules'](ast, elementName); - } - template['_styleAst'] = ast; - } - let ownPropertyNames = []; - if (!nativeCssVariables) { - ownPropertyNames = StyleProperties.decorateStyles(template['_styleAst']); - } - if (!ownPropertyNames.length || nativeCssVariables) { - let root = nativeShadow ? template.content : null; - let placeholder = getStylePlaceholder(elementName); - let style = this._generateStaticStyle(info, template['_styleAst'], root, placeholder, cssBuild, optimalBuild ? cssText : ''); - template._style = style; - } - template._ownPropertyNames = ownPropertyNames; - } - - /** - * @param {!Array<string>} cssTextArray - * @param {string} elementName - */ - prepareAdoptedCssText(cssTextArray, elementName) { - adoptedCssTextMap[elementName] = cssTextArray.join(' '); - } - /** - * Prepare template for the given element type - * @param {!HTMLTemplateElement} template - * @param {string} elementName - */ - prepareTemplateDom(template, elementName) { - if (disableRuntime) { - return; - } - const cssBuild = StyleUtil.getCssBuild(template); - if (!nativeShadow && cssBuild !== 'shady' && !template._domPrepared) { - template._domPrepared = true; - StyleTransformer.domAddScope(template.content, elementName); - } - } - /** - * @param {!{is: string, extends: (string|undefined)}} info - * @param {!StyleNode} rules - * @param {DocumentFragment} shadowroot - * @param {Node} placeholder - * @param {string} cssBuild - * @param {string=} cssText - * @return {?HTMLStyleElement} - */ - _generateStaticStyle(info, rules, shadowroot, placeholder, cssBuild, cssText) { - cssText = StyleTransformer.elementStyles(info, rules, null, cssBuild, cssText); - if (cssText.length) { - return StyleUtil.applyCss(cssText, info.is, shadowroot, placeholder); - } - return null; - } - _prepareHost(host) { - const {is, typeExtension} = StyleUtil.getIsExtends(host); - const placeholder = getStylePlaceholder(is); - const template = templateMap[is]; - if (!template) { - return; - } - const ast = template['_styleAst']; - const ownStylePropertyNames = template._ownPropertyNames; - const cssBuild = StyleUtil.getCssBuild(template); - const styleInfo = new StyleInfo( - ast, - placeholder, - ownStylePropertyNames, - is, - typeExtension, - cssBuild - ); - StyleInfo.set(host, styleInfo); - return styleInfo; - } - _ensureApplyShim() { - if (this._applyShim) { - return; - } else if (window.ShadyCSS && window.ShadyCSS.ApplyShim) { - this._applyShim = /** @type {!Object} */ (window.ShadyCSS.ApplyShim); - this._applyShim['invalidCallback'] = ApplyShimUtils.invalidate; - } - } - _ensureCustomStyleInterface() { - if (this._customStyleInterface) { - return; - } else if (window.ShadyCSS && window.ShadyCSS.CustomStyleInterface) { - this._customStyleInterface = /** @type {!CustomStyleInterfaceInterface} */(window.ShadyCSS.CustomStyleInterface); - /** @type {function(!HTMLStyleElement)} */ - this._customStyleInterface['transformCallback'] = (style) => {this.transformCustomStyleForDocument(style)}; - this._customStyleInterface['validateCallback'] = () => { - requestAnimationFrame(() => { - if (this._customStyleInterface['enqueued'] || this._elementsHaveApplied) { - this.flushCustomStyles(); - } - }) - }; - } - } - _ensure() { - this._ensureApplyShim(); - this._ensureCustomStyleInterface(); - } - /** - * Flush and apply custom styles to document - */ - flushCustomStyles() { - if (disableRuntime) { - return; - } - this._ensure(); - if (!this._customStyleInterface) { - return; - } - let customStyles = this._customStyleInterface['processStyles'](); - // early return if custom-styles don't need validation - if (!this._customStyleInterface['enqueued']) { - return; - } - // bail if custom styles are built optimally - if (StyleUtil.isOptimalCssBuild(this._documentOwnerStyleInfo.cssBuild)) { - return; - } - if (!nativeCssVariables) { - this._updateProperties(this._documentOwner, this._documentOwnerStyleInfo); - this._applyCustomStyles(customStyles); - if (this._elementsHaveApplied) { - // if custom elements have upgraded and there are no native css variables, we must recalculate the whole tree - this.styleDocument(); - } - } else if (!this._documentOwnerStyleInfo.cssBuild) { - this._revalidateCustomStyleApplyShim(customStyles); - } - this._customStyleInterface['enqueued'] = false; - } - /** - * Apply styles for the given element - * - * @param {!HTMLElement} host - * @param {Object=} overrideProps - */ - styleElement(host, overrideProps) { - if (disableRuntime) { - if (overrideProps) { - if (!StyleInfo.get(host)) { - StyleInfo.set(host, new StyleInfo(null)); - } - const styleInfo = /** @type {!StyleInfo} */(StyleInfo.get(host)); - this._mixOverrideStyleProps(styleInfo, overrideProps); - this.styleElementNativeVariables(host, styleInfo); - } - return; - } - const styleInfo = StyleInfo.get(host) || this._prepareHost(host); - // if there is no style info at this point, bail - if (!styleInfo) { - return; - } - // Only trip the `elementsHaveApplied` flag if a node other that the root document has `applyStyle` called - if (!this._isRootOwner(host)) { - this._elementsHaveApplied = true; - } - if (overrideProps) { - this._mixOverrideStyleProps(styleInfo, overrideProps); - } - if (!nativeCssVariables) { - this.styleElementShimVariables(host, styleInfo); - } else { - this.styleElementNativeVariables(host, styleInfo); - } - } - /** - * @param {!StyleInfo} styleInfo - * @param {Object} overrideProps - */ - _mixOverrideStyleProps(styleInfo, overrideProps) { - styleInfo.overrideStyleProperties = - styleInfo.overrideStyleProperties || {}; - Object.assign(styleInfo.overrideStyleProperties, overrideProps); - } - /** - * @param {!HTMLElement} host - * @param {!StyleInfo} styleInfo - */ - styleElementShimVariables(host, styleInfo) { - this.flush(); - this._updateProperties(host, styleInfo); - if (styleInfo.ownStylePropertyNames && styleInfo.ownStylePropertyNames.length) { - this._applyStyleProperties(host, styleInfo); - } - } - /** - * @param {!HTMLElement} host - * @param {!StyleInfo} styleInfo - */ - styleElementNativeVariables(host, styleInfo) { - const { is } = StyleUtil.getIsExtends(host); - if (styleInfo.overrideStyleProperties) { - updateNativeProperties(host, styleInfo.overrideStyleProperties); - } - const template = templateMap[is]; - // bail early if there is no shadowroot for this element - if (!template && !this._isRootOwner(host)) { - return; - } - // bail early if the template was built with polymer-css-build - if (template && StyleUtil.elementHasBuiltCss(template)) { - return; - } - if (template && template._style && !ApplyShimUtils.templateIsValid(template)) { - // update template - if (!ApplyShimUtils.templateIsValidating(template)) { - this._ensure(); - this._applyShim && this._applyShim['transformRules'](template['_styleAst'], is); - template._style.textContent = StyleTransformer.elementStyles(host, styleInfo.styleRules); - ApplyShimUtils.startValidatingTemplate(template); - } - // update instance if native shadowdom - if (nativeShadow) { - let root = host.shadowRoot; - if (root) { - let style = root.querySelector('style'); - if (style) { - style.textContent = StyleTransformer.elementStyles(host, styleInfo.styleRules); - } - } - } - styleInfo.styleRules = template['_styleAst']; - } - } - _styleOwnerForNode(node) { - let root = StyleUtil.wrap(node).getRootNode(); - let host = root.host; - if (host) { - if (StyleInfo.get(host) || this._prepareHost(host)) { - return host; - } else { - return this._styleOwnerForNode(host); - } - } - return this._documentOwner; - } - _isRootOwner(node) { - return (node === this._documentOwner); - } - _applyStyleProperties(host, styleInfo) { - let is = StyleUtil.getIsExtends(host).is; - let cacheEntry = styleCache.fetch(is, styleInfo.styleProperties, styleInfo.ownStylePropertyNames); - let cachedScopeSelector = cacheEntry && cacheEntry.scopeSelector; - let cachedStyle = cacheEntry ? cacheEntry.styleElement : null; - let oldScopeSelector = styleInfo.scopeSelector; - // only generate new scope if cached style is not found - styleInfo.scopeSelector = cachedScopeSelector || this._generateScopeSelector(is); - let style = StyleProperties.applyElementStyle(host, styleInfo.styleProperties, styleInfo.scopeSelector, cachedStyle); - if (!nativeShadow) { - StyleProperties.applyElementScopeSelector(host, styleInfo.scopeSelector, oldScopeSelector); - } - if (!cacheEntry) { - styleCache.store(is, styleInfo.styleProperties, style, styleInfo.scopeSelector); - } - return style; - } - _updateProperties(host, styleInfo) { - let owner = this._styleOwnerForNode(host); - let ownerStyleInfo = StyleInfo.get(owner); - let ownerProperties = ownerStyleInfo.styleProperties; - // style owner has not updated properties yet - // go up the chain and force property update, - // except if the owner is the document - if (owner !== this._documentOwner && !ownerProperties) { - this._updateProperties(owner, ownerStyleInfo); - ownerProperties = ownerStyleInfo.styleProperties; - } - let props = Object.create(ownerProperties || null); - let hostAndRootProps = StyleProperties.hostAndRootPropertiesForScope(host, styleInfo.styleRules, styleInfo.cssBuild); - let propertyData = StyleProperties.propertyDataFromStyles(ownerStyleInfo.styleRules, host); - let propertiesMatchingHost = propertyData.properties - Object.assign( - props, - hostAndRootProps.hostProps, - propertiesMatchingHost, - hostAndRootProps.rootProps - ); - this._mixinOverrideStyles(props, styleInfo.overrideStyleProperties); - StyleProperties.reify(props); - styleInfo.styleProperties = props; - } - _mixinOverrideStyles(props, overrides) { - for (let p in overrides) { - let v = overrides[p]; - // skip override props if they are not truthy or 0 - // in order to fall back to inherited values - if (v || v === 0) { - props[p] = v; - } - } - } - /** - * Update styles of the whole document - * - * @param {Object=} properties - */ - styleDocument(properties) { - this.styleSubtree(this._documentOwner, properties); - } - /** - * Update styles of a subtree - * - * @param {!HTMLElement} host - * @param {Object=} properties - */ - styleSubtree(host, properties) { - const wrappedHost = StyleUtil.wrap(host); - let root = wrappedHost.shadowRoot; - if (root || this._isRootOwner(host)) { - this.styleElement(host, properties); - } - // process the shadowdom children of `host` - let shadowChildren = - root && (/** @type {!ParentNode} */ (root).children || root.childNodes); - if (shadowChildren) { - for (let i = 0; i < shadowChildren.length; i++) { - let c = /** @type {!HTMLElement} */(shadowChildren[i]); - this.styleSubtree(c); - } - } else { - // process the lightdom children of `host` - let children = wrappedHost.children || wrappedHost.childNodes; - if (children) { - for (let i = 0; i < children.length; i++) { - let c = /** @type {!HTMLElement} */(children[i]); - this.styleSubtree(c); - } - } - } - } - /* Custom Style operations */ - _revalidateCustomStyleApplyShim(customStyles) { - for (let i = 0; i < customStyles.length; i++) { - let c = customStyles[i]; - let s = this._customStyleInterface['getStyleForCustomStyle'](c); - if (s) { - this._revalidateApplyShim(s); - } - } - } - _applyCustomStyles(customStyles) { - for (let i = 0; i < customStyles.length; i++) { - let c = customStyles[i]; - let s = this._customStyleInterface['getStyleForCustomStyle'](c); - if (s) { - StyleProperties.applyCustomStyle(s, this._documentOwnerStyleInfo.styleProperties); - } - } - } - transformCustomStyleForDocument(style) { - const cssBuild = StyleUtil.getCssBuild(style); - if (cssBuild !== this._documentOwnerStyleInfo.cssBuild) { - this._documentOwnerStyleInfo.cssBuild = cssBuild; - } - if (StyleUtil.isOptimalCssBuild(cssBuild)) { - return; - } - let ast = StyleUtil.rulesForStyle(style); - StyleUtil.forEachRule(ast, (rule) => { - if (nativeShadow) { - StyleTransformer.normalizeRootSelector(rule); - } else { - StyleTransformer.documentRule(rule); - } - if (nativeCssVariables && cssBuild === '') { - this._ensure(); - this._applyShim && this._applyShim['transformRule'](rule); - } - }); - if (nativeCssVariables) { - style.textContent = StyleUtil.toCssText(ast); - } else { - this._documentOwnerStyleInfo.styleRules['rules'].push(ast); - } - } - _revalidateApplyShim(style) { - if (nativeCssVariables && this._applyShim) { - let ast = StyleUtil.rulesForStyle(style); - this._ensure(); - this._applyShim['transformRules'](ast); - style.textContent = StyleUtil.toCssText(ast); - } - } - getComputedStyleValue(element, property) { - let value; - if (!nativeCssVariables) { - // element is either a style host, or an ancestor of a style host - let styleInfo = StyleInfo.get(element) || StyleInfo.get(this._styleOwnerForNode(element)); - value = styleInfo.styleProperties[property]; - } - // fall back to the property value from the computed styling - value = value || window.getComputedStyle(element).getPropertyValue(property); - // trim whitespace that can come after the `:` in css - // example: padding: 2px -> " 2px" - return value ? value.trim() : ''; - } - // given an element and a classString, replaces - // the element's class with the provided classString and adds - // any necessary ShadyCSS static and property based scoping selectors - setElementClass(element, classString) { - let root = StyleUtil.wrap(element).getRootNode(); - let classes = classString ? classString.split(/\s/) : []; - let scopeName = root.host && root.host.localName; - // If no scope, try to discover scope name from existing class. - // This can occur if, for example, a template stamped element that - // has been scoped is manipulated when not in a root. - if (!scopeName) { - var classAttr = element.getAttribute('class'); - if (classAttr) { - let k$ = classAttr.split(/\s/); - for (let i=0; i < k$.length; i++) { - if (k$[i] === StyleTransformer.SCOPE_NAME) { - scopeName = k$[i+1]; - break; - } - } - } - } - if (scopeName) { - classes.push(StyleTransformer.SCOPE_NAME, scopeName); - } - if (!nativeCssVariables) { - let styleInfo = StyleInfo.get(element); - if (styleInfo && styleInfo.scopeSelector) { - classes.push(StyleProperties.XSCOPE_NAME, styleInfo.scopeSelector); - } - } - StyleUtil.setElementClassRaw(element, classes.join(' ')); - } - _styleInfoForNode(node) { - return StyleInfo.get(node); - } - /** - * @param {!Element} node - * @param {string} scope - */ - scopeNode(node, scope) { - StyleTransformer.element(node, scope); - } - /** - * @param {!Element} node - * @param {string} scope - */ - unscopeNode(node, scope) { - StyleTransformer.element(node, scope, true); - } - /** - * @param {!Node} node - * @return {string} - */ - scopeForNode(node) { - return getOwnerScope(node); - } - /** - * @param {!Element} node - * @return {string} - */ - currentScopeForNode(node) { - return getCurrentScope(node); - } -} - -/* exports */ -/* eslint-disable no-self-assign */ -ScopingShim.prototype['flush'] = ScopingShim.prototype.flush; -ScopingShim.prototype['prepareTemplate'] = ScopingShim.prototype.prepareTemplate; -ScopingShim.prototype['styleElement'] = ScopingShim.prototype.styleElement; -ScopingShim.prototype['styleDocument'] = ScopingShim.prototype.styleDocument; -ScopingShim.prototype['styleSubtree'] = ScopingShim.prototype.styleSubtree; -ScopingShim.prototype['getComputedStyleValue'] = ScopingShim.prototype.getComputedStyleValue; -ScopingShim.prototype['setElementClass'] = ScopingShim.prototype.setElementClass; -ScopingShim.prototype['_styleInfoForNode'] = ScopingShim.prototype._styleInfoForNode; -ScopingShim.prototype['transformCustomStyleForDocument'] = ScopingShim.prototype.transformCustomStyleForDocument; -ScopingShim.prototype['getStyleAst'] = ScopingShim.prototype.getStyleAst; -ScopingShim.prototype['styleAstToString'] = ScopingShim.prototype.styleAstToString; -ScopingShim.prototype['flushCustomStyles'] = ScopingShim.prototype.flushCustomStyles; -ScopingShim.prototype['scopeNode'] = ScopingShim.prototype.scopeNode; -ScopingShim.prototype['unscopeNode'] = ScopingShim.prototype.unscopeNode; -ScopingShim.prototype['scopeForNode'] = ScopingShim.prototype.scopeForNode; -ScopingShim.prototype['currentScopeForNode'] = ScopingShim.prototype.currentScopeForNode; -ScopingShim.prototype['prepareAdoptedCssText'] = ScopingShim.prototype.prepareAdoptedCssText; -/* eslint-enable no-self-assign */ -Object.defineProperties(ScopingShim.prototype, { - 'nativeShadow': { - get() { - return nativeShadow; - } - }, - 'nativeCss': { - get() { - return nativeCssVariables; - } - } -});
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-cache.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-cache.js deleted file mode 100644 index 0006a0b..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-cache.js +++ /dev/null
@@ -1,52 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ -'use strict'; - -export default class StyleCache { - constructor(typeMax = 100) { - // map element name -> [{properties, styleElement, scopeSelector}] - this.cache = {}; - /** @type {number} */ - this.typeMax = typeMax; - } - - _validate(cacheEntry, properties, ownPropertyNames) { - for (let idx = 0; idx < ownPropertyNames.length; idx++) { - let pn = ownPropertyNames[idx]; - if (cacheEntry.properties[pn] !== properties[pn]) { - return false; - } - } - return true; - } - - store(tagname, properties, styleElement, scopeSelector) { - let list = this.cache[tagname] || []; - list.push({properties, styleElement, scopeSelector}); - if (list.length > this.typeMax) { - list.shift(); - } - this.cache[tagname] = list; - } - - fetch(tagname, properties, ownPropertyNames) { - let list = this.cache[tagname]; - if (!list) { - return; - } - // reverse list for most-recent lookups - for (let idx = list.length - 1; idx >= 0; idx--) { - let entry = list[idx]; - if (this._validate(entry, properties, ownPropertyNames)) { - return entry; - } - } - } -}
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-info.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-info.js deleted file mode 100644 index deab329..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-info.js +++ /dev/null
@@ -1,75 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars - -/** @const {string} */ -const infoKey = '__styleInfo'; - -export default class StyleInfo { - /** - * @param {Element} node - * @return {StyleInfo} - */ - static get(node) { - if (node) { - return node[infoKey]; - } else { - return null; - } - } - /** - * @param {!Element} node - * @param {StyleInfo} styleInfo - * @return {StyleInfo} - */ - static set(node, styleInfo) { - node[infoKey] = styleInfo; - return styleInfo; - } - /** - * @param {StyleNode} ast - * @param {Node=} placeholder - * @param {Array<string>=} ownStylePropertyNames - * @param {string=} elementName - * @param {string=} typeExtension - * @param {string=} cssBuild - */ - constructor(ast, placeholder, ownStylePropertyNames, elementName, typeExtension, cssBuild) { - /** @type {StyleNode} */ - this.styleRules = ast || null; - /** @type {Node} */ - this.placeholder = placeholder || null; - /** @type {!Array<string>} */ - this.ownStylePropertyNames = ownStylePropertyNames || []; - /** @type {Object} */ - this.overrideStyleProperties = null; - /** @type {string} */ - this.elementName = elementName || ''; - /** @type {string} */ - this.cssBuild = cssBuild || ''; - /** @type {string} */ - this.typeExtension = typeExtension || ''; - /** @type {Object<string, string>} */ - this.styleProperties = null; - /** @type {?string} */ - this.scopeSelector = null; - /** @type {HTMLStyleElement} */ - this.customStyle = null; - } - _getStyleRules() { - return this.styleRules; - } -} - -/* eslint-disable-next-line no-self-assign */ -StyleInfo.prototype['_getStyleRules'] = StyleInfo.prototype._getStyleRules;
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-placeholder.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-placeholder.js deleted file mode 100644 index d003379..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-placeholder.js +++ /dev/null
@@ -1,55 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -import {applyStylePlaceHolder} from './style-util.js'; -import {nativeShadow, disableRuntime} from './style-settings.js'; - -/** @type {!Object<string, !Node>} */ -const placeholderMap = {}; - -/** - * @param {string} elementName - * @return {Node} - */ -export function getStylePlaceholder(elementName) { - return placeholderMap[elementName] || null; -} - -/** - * @param {string} elementName - */ -export function ensureStylePlaceholder(elementName) { - if (!placeholderMap[elementName]) { - placeholderMap[elementName] = applyStylePlaceHolder(elementName); - } -} - -/** - * @const {CustomElementRegistry} - */ -const ce = window['customElements']; -if (ce && !nativeShadow && !disableRuntime) { - /** - * @const {function(this:CustomElementRegistry, string,function(new:HTMLElement),{extends: string}=)} - */ - const origDefine = ce['define']; - /** - * @param {string} name - * @param {function(new:HTMLElement)} clazz - * @param {{extends: string}=} options - */ - const wrappedDefine = (name, clazz, options) => { - ensureStylePlaceholder(name); - origDefine.call(/** @type {!CustomElementRegistry} */(ce), name, clazz, options); - }; - ce['define'] = wrappedDefine; -} \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-properties.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-properties.js deleted file mode 100644 index ecaa389..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-properties.js +++ /dev/null
@@ -1,608 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -import {removeCustomPropAssignment, StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars -import {nativeShadow} from './style-settings.js'; -import StyleTransformer from './style-transformer.js'; -import * as StyleUtil from './style-util.js'; -import * as RX from './common-regex.js'; -import StyleInfo from './style-info.js'; - -// TODO: dedupe with shady -/** - * @param {string} selector - * @return {boolean} - * @this {Element} - */ -const matchesSelector = function(selector) { - const method = this.matches || this.matchesSelector || - this.mozMatchesSelector || this.msMatchesSelector || - this.oMatchesSelector || this.webkitMatchesSelector; - return method && method.call(this, selector); -}; - -const IS_IE = navigator.userAgent.match('Trident'); - -const XSCOPE_NAME = 'x-scope'; - -class StyleProperties { - get XSCOPE_NAME() { - return XSCOPE_NAME; - } -/** - * decorates styles with rule info and returns an array of used style property names - * - * @param {StyleNode} rules - * @return {Array<string>} - */ - decorateStyles(rules) { - let self = this, props = {}, keyframes = [], ruleIndex = 0; - StyleUtil.forEachRule(rules, function(rule) { - self.decorateRule(rule); - // mark in-order position of ast rule in styles block, used for cache key - rule.index = ruleIndex++; - self.collectPropertiesInCssText(rule.propertyInfo.cssText, props); - }, function onKeyframesRule(rule) { - keyframes.push(rule); - }); - // Cache all found keyframes rules for later reference: - rules._keyframes = keyframes; - // return this list of property names *consumes* in these styles. - let names = []; - for (let i in props) { - names.push(i); - } - return names; - } - - // decorate a single rule with property info - decorateRule(rule) { - if (rule.propertyInfo) { - return rule.propertyInfo; - } - let info = {}, properties = {}; - let hasProperties = this.collectProperties(rule, properties); - if (hasProperties) { - info.properties = properties; - // TODO(sorvell): workaround parser seeing mixins as additional rules - rule['rules'] = null; - } - info.cssText = this.collectCssText(rule); - rule.propertyInfo = info; - return info; - } - - // collects the custom properties from a rule's cssText - collectProperties(rule, properties) { - let info = rule.propertyInfo; - if (info) { - if (info.properties) { - Object.assign(properties, info.properties); - return true; - } - } else { - let m, rx = RX.VAR_ASSIGN; - let cssText = rule['parsedCssText']; - let value; - let any; - while ((m = rx.exec(cssText))) { - // note: group 2 is var, 3 is mixin - value = (m[2] || m[3]).trim(); - // value of 'inherit' or 'unset' is equivalent to not setting the property here - if (value !== 'inherit' || value !== 'unset') { - properties[m[1].trim()] = value; - } - any = true; - } - return any; - } - - } - - // returns cssText of properties that consume variables/mixins - collectCssText(rule) { - return this.collectConsumingCssText(rule['parsedCssText']); - } - - // NOTE: we support consumption inside mixin assignment - // but not production, so strip out {...} - collectConsumingCssText(cssText) { - return cssText.replace(RX.BRACKETED, '') - .replace(RX.VAR_ASSIGN, ''); - } - - collectPropertiesInCssText(cssText, props) { - let m; - while ((m = RX.VAR_CONSUMED.exec(cssText))) { - let name = m[1]; - // This regex catches all variable names, and following non-whitespace char - // If next char is not ':', then variable is a consumer - if (m[2] !== ':') { - props[name] = true; - } - } - } - - // turns custom properties into realized values. - reify(props) { - // big perf optimization here: reify only *own* properties - // since this object has __proto__ of the element's scope properties - let names = Object.getOwnPropertyNames(props); - for (let i=0, n; i < names.length; i++) { - n = names[i]; - props[n] = this.valueForProperty(props[n], props); - } - } - - // given a property value, returns the reified value - // a property value may be: - // (1) a literal value like: red or 5px; - // (2) a variable value like: var(--a), var(--a, red), or var(--a, --b) or - // var(--a, var(--b)); - // (3) a literal mixin value like { properties }. Each of these properties - // can have values that are: (a) literal, (b) variables, (c) @apply mixins. - valueForProperty(property, props) { - // case (1) default - // case (3) defines a mixin and we have to reify the internals - if (property) { - if (property.indexOf(';') >=0) { - property = this.valueForProperties(property, props); - } else { - // case (2) variable - let self = this; - let fn = function(prefix, value, fallback, suffix) { - if (!value) { - return prefix + suffix; - } - let propertyValue = self.valueForProperty(props[value], props); - // if value is "initial", then the variable should be treated as unset - if (!propertyValue || propertyValue === 'initial') { - // fallback may be --a or var(--a) or literal - propertyValue = self.valueForProperty(props[fallback] || fallback, props) || - fallback; - } else if (propertyValue === 'apply-shim-inherit') { - // CSS build will replace `inherit` with `apply-shim-inherit` - // for use with native css variables. - // Since we have full control, we can use `inherit` directly. - propertyValue = 'inherit'; - } - return prefix + (propertyValue || '') + suffix; - }; - property = StyleUtil.processVariableAndFallback(property, fn); - } - } - return property && property.trim() || ''; - } - - // note: we do not yet support mixin within mixin - valueForProperties(property, props) { - let parts = property.split(';'); - for (let i=0, p, m; i<parts.length; i++) { - if ((p = parts[i])) { - RX.MIXIN_MATCH.lastIndex = 0; - m = RX.MIXIN_MATCH.exec(p); - if (m) { - p = this.valueForProperty(props[m[1]], props); - } else { - let colon = p.indexOf(':'); - if (colon !== -1) { - let pp = p.substring(colon); - pp = pp.trim(); - pp = this.valueForProperty(pp, props) || pp; - p = p.substring(0, colon) + pp; - } - } - parts[i] = (p && p.lastIndexOf(';') === p.length - 1) ? - // strip trailing ; - p.slice(0, -1) : - p || ''; - } - } - return parts.join(';'); - } - - applyProperties(rule, props) { - let output = ''; - // dynamically added sheets may not be decorated so ensure they are. - if (!rule.propertyInfo) { - this.decorateRule(rule); - } - if (rule.propertyInfo.cssText) { - output = this.valueForProperties(rule.propertyInfo.cssText, props); - } - rule['cssText'] = output; - } - - // Apply keyframe transformations to the cssText of a given rule. The - // keyframeTransforms object is a map of keyframe names to transformer - // functions which take in cssText and spit out transformed cssText. - applyKeyframeTransforms(rule, keyframeTransforms) { - let input = rule['cssText']; - let output = rule['cssText']; - if (rule.hasAnimations == null) { - // Cache whether or not the rule has any animations to begin with: - rule.hasAnimations = RX.ANIMATION_MATCH.test(input); - } - // If there are no animations referenced, we can skip transforms: - if (rule.hasAnimations) { - let transform; - // If we haven't transformed this rule before, we iterate over all - // transforms: - if (rule.keyframeNamesToTransform == null) { - rule.keyframeNamesToTransform = []; - for (let keyframe in keyframeTransforms) { - transform = keyframeTransforms[keyframe]; - output = transform(input); - // If the transform actually changed the CSS text, we cache the - // transform name for future use: - if (input !== output) { - input = output; - rule.keyframeNamesToTransform.push(keyframe); - } - } - } else { - // If we already have a list of keyframe names that apply to this - // rule, we apply only those keyframe name transforms: - for (let i = 0; i < rule.keyframeNamesToTransform.length; ++i) { - transform = keyframeTransforms[rule.keyframeNamesToTransform[i]]; - input = transform(input); - } - output = input; - } - } - rule['cssText'] = output; - } - - // Test if the rules in these styles matches the given `element` and if so, - // collect any custom properties into `props`. - /** - * @param {StyleNode} rules - * @param {Element} element - */ - propertyDataFromStyles(rules, element) { - let props = {}; - // generates a unique key for these matches - let o = []; - // note: active rules excludes non-matching @media rules - StyleUtil.forEachRule(rules, (rule) => { - // TODO(sorvell): we could trim the set of rules at declaration - // time to only include ones that have properties - if (!rule.propertyInfo) { - this.decorateRule(rule); - } - // match element against transformedSelector: selector may contain - // unwanted uniquification and parsedSelector does not directly match - // for :host selectors. - let selectorToMatch = rule.transformedSelector || rule['parsedSelector']; - if (element && rule.propertyInfo.properties && selectorToMatch) { - if (matchesSelector.call(element, selectorToMatch)) { - this.collectProperties(rule, props); - // produce numeric key for these matches for lookup - addToBitMask(rule.index, o); - } - } - }, null, true); - return {properties: props, key: o}; - } - - /** - * @param {Element} scope - * @param {StyleNode} rule - * @param {string} cssBuild - * @param {function(Object)} callback - */ - whenHostOrRootRule(scope, rule, cssBuild, callback) { - if (!rule.propertyInfo) { - this.decorateRule(rule); - } - if (!rule.propertyInfo.properties) { - return; - } - let {is, typeExtension} = StyleUtil.getIsExtends(scope); - let hostScope = is ? - StyleTransformer._calcHostScope(is, typeExtension) : - 'html'; - let parsedSelector = rule['parsedSelector']; - let isRoot = (parsedSelector === ':host > *' || parsedSelector === 'html'); - let isHost = parsedSelector.indexOf(':host') === 0 && !isRoot; - // build info is either in scope (when scope is an element) or in the style - // when scope is the default scope; note: this allows default scope to have - // mixed mode built and unbuilt styles. - if (cssBuild === 'shady') { - // :root -> x-foo > *.x-foo for elements and html for custom-style - isRoot = parsedSelector === (hostScope + ' > *.' + hostScope) || parsedSelector.indexOf('html') !== -1; - // :host -> x-foo for elements, but sub-rules have .x-foo in them - isHost = !isRoot && parsedSelector.indexOf(hostScope) === 0; - } - if (!isRoot && !isHost) { - return; - } - let selectorToMatch = hostScope; - if (isHost) { - // need to transform :host because `:host` does not work with `matches` - if (!rule.transformedSelector) { - // transform :host into a matchable selector - rule.transformedSelector = - StyleTransformer._transformRuleCss( - rule, - StyleTransformer._transformComplexSelector, - StyleTransformer._calcElementScope(is), - hostScope - ); - } - selectorToMatch = rule.transformedSelector || hostScope; - } - callback({ - selector: selectorToMatch, - isHost: isHost, - isRoot: isRoot - }); - } -/** - * @param {Element} scope - * @param {StyleNode} rules - * @param {string} cssBuild - * @return {Object} - */ - hostAndRootPropertiesForScope(scope, rules, cssBuild) { - let hostProps = {}, rootProps = {}; - // note: active rules excludes non-matching @media rules - StyleUtil.forEachRule(rules, (rule) => { - // if scope is StyleDefaults, use _element for matchesSelector - this.whenHostOrRootRule(scope, rule, cssBuild, (info) => { - let element = scope._element || scope; - if (matchesSelector.call(element, info.selector)) { - if (info.isHost) { - this.collectProperties(rule, hostProps); - } else { - this.collectProperties(rule, rootProps); - } - } - }); - }, null, true); - return {rootProps: rootProps, hostProps: hostProps}; - } - - /** - * @param {Element} element - * @param {Object} properties - * @param {string} scopeSelector - */ - transformStyles(element, properties, scopeSelector) { - let self = this; - let {is, typeExtension} = StyleUtil.getIsExtends(element); - let hostSelector = StyleTransformer - ._calcHostScope(is, typeExtension); - let rxHostSelector = element.extends ? - '\\' + hostSelector.slice(0, -1) + '\\]' : - hostSelector; - let hostRx = new RegExp(RX.HOST_PREFIX + rxHostSelector + - RX.HOST_SUFFIX); - let {styleRules: rules, cssBuild} = StyleInfo.get(element); - let keyframeTransforms = - this._elementKeyframeTransforms(element, rules, scopeSelector); - return StyleTransformer.elementStyles(element, rules, function(rule) { - self.applyProperties(rule, properties); - if (!nativeShadow && - !StyleUtil.isKeyframesSelector(rule) && - rule['cssText']) { - // NOTE: keyframe transforms only scope munge animation names, so it - // is not necessary to apply them in ShadowDOM. - self.applyKeyframeTransforms(rule, keyframeTransforms); - self._scopeSelector(rule, hostRx, hostSelector, scopeSelector); - } - }, cssBuild); - } - - /** - * @param {Element} element - * @param {StyleNode} rules - * @param {string} scopeSelector - * @return {Object} - */ - _elementKeyframeTransforms(element, rules, scopeSelector) { - let keyframesRules = rules._keyframes; - let keyframeTransforms = {}; - if (!nativeShadow && keyframesRules) { - // For non-ShadowDOM, we transform all known keyframes rules in - // advance for the current scope. This allows us to catch keyframes - // rules that appear anywhere in the stylesheet: - for (let i = 0, keyframesRule = keyframesRules[i]; - i < keyframesRules.length; - keyframesRule = keyframesRules[++i]) { - this._scopeKeyframes(keyframesRule, scopeSelector); - keyframeTransforms[keyframesRule['keyframesName']] = - this._keyframesRuleTransformer(keyframesRule); - } - } - return keyframeTransforms; - } - - // Generate a factory for transforming a chunk of CSS text to handle a - // particular scoped keyframes rule. - /** - * @param {StyleNode} keyframesRule - * @return {function(string):string} - */ - _keyframesRuleTransformer(keyframesRule) { - return function(cssText) { - return cssText.replace( - keyframesRule.keyframesNameRx, - keyframesRule.transformedKeyframesName); - }; - } - -/** - * Transforms `@keyframes` names to be unique for the current host. - * Example: @keyframes foo-anim -> @keyframes foo-anim-x-foo-0 - * - * @param {StyleNode} rule - * @param {string} scopeId - */ - _scopeKeyframes(rule, scopeId) { - // Animation names are of the form [\w-], so ensure that the name regex does not partially apply - // to similarly named keyframe names by checking for a word boundary at the beginning and - // a non-word boundary or `-` at the end. - rule.keyframesNameRx = new RegExp(`\\b${rule['keyframesName']}(?!\\B|-)`, 'g'); - rule.transformedKeyframesName = rule['keyframesName'] + '-' + scopeId; - rule.transformedSelector = rule.transformedSelector || rule['selector']; - rule['selector'] = rule.transformedSelector.replace( - rule['keyframesName'], rule.transformedKeyframesName); - } - - // Strategy: x scope shim a selector e.g. to scope `.x-foo-42` (via classes): - // non-host selector: .a.x-foo -> .x-foo-42 .a.x-foo - // host selector: x-foo.wide -> .x-foo-42.wide - // note: we use only the scope class (.x-foo-42) and not the hostSelector - // (x-foo) to scope :host rules; this helps make property host rules - // have low specificity. They are overrideable by class selectors but, - // unfortunately, not by type selectors (e.g. overriding via - // `.special` is ok, but not by `x-foo`). - /** - * @param {StyleNode} rule - * @param {RegExp} hostRx - * @param {string} hostSelector - * @param {string} scopeId - */ - _scopeSelector(rule, hostRx, hostSelector, scopeId) { - rule.transformedSelector = rule.transformedSelector || rule['selector']; - let selector = rule.transformedSelector; - let scope = '.' + scopeId; - let parts = StyleUtil.splitSelectorList(selector); - for (let i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) { - parts[i] = p.match(hostRx) ? - p.replace(hostSelector, scope) : - scope + ' ' + p; - } - rule['selector'] = parts.join(','); - } - - /** - * @param {Element} element - * @param {string} selector - * @param {string} old - */ - applyElementScopeSelector(element, selector, old) { - let c = element.getAttribute('class') || ''; - let v = c; - if (old) { - v = c.replace( - new RegExp('\\s*' + XSCOPE_NAME + '\\s*' + old + '\\s*', 'g'), ' '); - } - v += (v ? ' ' : '') + XSCOPE_NAME + ' ' + selector; - if (c !== v) { - StyleUtil.setElementClassRaw(element, v); - } - } - - /** - * @param {HTMLElement} element - * @param {Object} properties - * @param {string} selector - * @param {HTMLStyleElement} style - * @return {HTMLStyleElement} - */ - applyElementStyle(element, properties, selector, style) { - // calculate cssText to apply - let cssText = style ? style.textContent || '' : - this.transformStyles(element, properties, selector); - // if shady and we have a cached style that is not style, decrement - let styleInfo = StyleInfo.get(element); - let s = styleInfo.customStyle; - if (s && !nativeShadow && (s !== style)) { - s['_useCount']--; - if (s['_useCount'] <= 0 && s.parentNode) { - s.parentNode.removeChild(s); - } - } - // apply styling always under native or if we generated style - // or the cached style is not in document(!) - if (nativeShadow) { - // update existing style only under native - if (styleInfo.customStyle) { - styleInfo.customStyle.textContent = cssText; - style = styleInfo.customStyle; - // otherwise, if we have css to apply, do so - } else if (cssText) { - // apply css after the scope style of the element to help with - // style precedence rules. - style = StyleUtil.applyCss(cssText, selector, element.shadowRoot, - styleInfo.placeholder); - } - } else { - // shady and no cache hit - if (!style) { - // apply css after the scope style of the element to help with - // style precedence rules. - if (cssText) { - style = StyleUtil.applyCss(cssText, selector, null, - styleInfo.placeholder); - } - // shady and cache hit but not in document - } else if (!style.parentNode) { - if (IS_IE && cssText.indexOf('@media') > -1) { - // @media rules may be stale in IE 10 and 11 - // refresh the text content of the style to revalidate them. - style.textContent = cssText; - } - StyleUtil.applyStyle(style, null, styleInfo.placeholder); - } - } - // ensure this style is our custom style and increment its use count. - if (style) { - style['_useCount'] = style['_useCount'] || 0; - // increment use count if we changed styles - if (styleInfo.customStyle != style) { - style['_useCount']++; - } - styleInfo.customStyle = style; - } - return style; - } - - /** - * @param {Element} style - * @param {Object} properties - */ - applyCustomStyle(style, properties) { - let rules = StyleUtil.rulesForStyle(/** @type {HTMLStyleElement} */(style)); - let self = this; - style.textContent = StyleUtil.toCssText(rules, function(/** StyleNode */rule) { - let css = rule['cssText'] = rule['parsedCssText']; - if (rule.propertyInfo && rule.propertyInfo.cssText) { - // remove property assignments - // so next function isn't confused - // NOTE: we have 3 categories of css: - // (1) normal properties, - // (2) custom property assignments (--foo: red;), - // (3) custom property usage: border: var(--foo); @apply(--foo); - // In elements, 1 and 3 are separated for efficiency; here they - // are not and this makes this case unique. - css = removeCustomPropAssignment(/** @type {string} */(css)); - // replace with reified properties, scenario is same as mixin - rule['cssText'] = self.valueForProperties(css, properties); - } - }); - } -} - -/** - * @param {number} n - * @param {Array<number>} bits - */ -function addToBitMask(n, bits) { - let o = parseInt(n / 32, 10); - let v = 1 << (n % 32); - bits[o] = (bits[o] || 0) | v; -} - -export default new StyleProperties(); \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-settings.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-settings.js deleted file mode 100644 index c8fdd4b..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-settings.js +++ /dev/null
@@ -1,56 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -export const nativeShadow = !(window['ShadyDOM'] && window['ShadyDOM']['inUse']); -let nativeCssVariables_; - -/** - * @param {(ShadyCSSOptions | ShadyCSSInterface)=} settings - */ -function calcCssVariables(settings) { - if (settings && settings['shimcssproperties']) { - nativeCssVariables_ = false; - } else { - // chrome 49 has semi-working css vars, check if box-shadow works - // safari 9.1 has a recalc bug: https://bugs.webkit.org/show_bug.cgi?id=155782 - // However, shim css custom properties are only supported with ShadyDOM enabled, - // so fall back on native if we do not detect ShadyDOM - // Edge 15: custom properties used in ::before and ::after will also be used in the parent element - // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12414257/ - nativeCssVariables_ = nativeShadow || Boolean(!navigator.userAgent.match(/AppleWebKit\/601|Edge\/15/) && - window.CSS && CSS.supports && CSS.supports('box-shadow', '0 0 0 var(--foo)')); - } -} - -/** @type {string | undefined} */ -export let cssBuild; -if (window.ShadyCSS && window.ShadyCSS.cssBuild !== undefined) { - cssBuild = window.ShadyCSS.cssBuild; -} - -/** @type {boolean} */ -export const disableRuntime = Boolean(window.ShadyCSS && window.ShadyCSS.disableRuntime); - -if (window.ShadyCSS && window.ShadyCSS.nativeCss !== undefined) { - nativeCssVariables_ = window.ShadyCSS.nativeCss; -} else if (window.ShadyCSS) { - calcCssVariables(window.ShadyCSS); - // reset window variable to let ShadyCSS API take its place - window.ShadyCSS = undefined; -} else { - calcCssVariables(window['WebComponents'] && window['WebComponents']['flags']); -} - -// Hack for type error under new type inference which doesn't like that -// nativeCssVariables is updated in a function and assigns the type -// `function(): ?` instead of `boolean`. -export const nativeCssVariables = /** @type {boolean} */(nativeCssVariables_); \ No newline at end of file
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-transformer.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-transformer.js deleted file mode 100644 index 6803a8b..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-transformer.js +++ /dev/null
@@ -1,487 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -import {StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars -import * as StyleUtil from './style-util.js'; -import {nativeShadow} from './style-settings.js'; - -/* Transforms ShadowDOM styling into ShadyDOM styling - -* scoping: - - * elements in scope get scoping selector class="x-foo-scope" - * selectors re-written as follows: - - div button -> div.x-foo-scope button.x-foo-scope - -* :host -> scopeName - -* :host(...) -> scopeName... - -* ::slotted(...) -> scopeName > ... - -* ...:dir(ltr|rtl) -> [dir="ltr|rtl"] ..., ...[dir="ltr|rtl"] - -* :host(:dir[rtl]) -> scopeName:dir(rtl) -> [dir="rtl"] scopeName, scopeName[dir="rtl"] - -*/ -const SCOPE_NAME = 'style-scope'; - -class StyleTransformer { - get SCOPE_NAME() { - return SCOPE_NAME; - } - /** - * Given a node and scope name, add a scoping class to each node - * in the tree. This facilitates transforming css into scoped rules. - * @param {!Node} node - * @param {string} scope - * @param {boolean=} shouldRemoveScope - * @deprecated - */ - dom(node, scope, shouldRemoveScope) { - const fn = (node) => { - this.element(node, scope || '', shouldRemoveScope); - }; - this._transformDom(node, fn); - } - - /** - * Given a node and scope name, add a scoping class to each node in the tree. - * @param {!Node} node - * @param {string} scope - */ - domAddScope(node, scope) { - const fn = (node) => { - this.element(node, scope || ''); - }; - this._transformDom(node, fn); - } - - /** - * @param {!Node} startNode - * @param {!function(!Node)} transformer - */ - _transformDom(startNode, transformer) { - if (startNode.nodeType === Node.ELEMENT_NODE) { - transformer(startNode) - } - let c$; - if (startNode.localName === 'template') { - const template = /** @type {!HTMLTemplateElement} */ (startNode); - // In case the template is in svg context, fall back to the node - // since it won't be an HTMLTemplateElement with a .content property - c$ = (template.content || template._content || template).childNodes; - } else { - c$ = /** @type {!ParentNode} */ (startNode).children || - startNode.childNodes; - } - if (c$) { - for (let i = 0; i < c$.length; i++) { - this._transformDom(c$[i], transformer); - } - } - } - - /** - * @param {?} element - * @param {?} scope - * @param {?=} shouldRemoveScope - */ - element(element, scope, shouldRemoveScope) { - // note: if using classes, we add both the general 'style-scope' class - // as well as the specific scope. This enables easy filtering of all - // `style-scope` elements - if (scope) { - // note: svg on IE does not have classList so fallback to class - if (element.classList) { - if (shouldRemoveScope) { - element.classList.remove(SCOPE_NAME); - element.classList.remove(scope); - } else { - element.classList.add(SCOPE_NAME); - element.classList.add(scope); - } - } else if (element.getAttribute) { - let c = element.getAttribute(CLASS); - if (shouldRemoveScope) { - if (c) { - let newValue = c.replace(SCOPE_NAME, '').replace(scope, ''); - StyleUtil.setElementClassRaw(element, newValue); - } - } else { - let newValue = (c ? c + ' ' : '') + SCOPE_NAME + ' ' + scope; - StyleUtil.setElementClassRaw(element, newValue); - } - } - } - } - - /** - * Given a node, replace the scoping class to each subnode in the tree. - * @param {!Node} node - * @param {string} oldScope - * @param {string} newScope - */ - domReplaceScope(node, oldScope, newScope) { - const fn = (node) => { - this.element(node, oldScope, true); - this.element(node, newScope); - }; - this._transformDom(node, fn); - } - /** - * Given a node, remove the scoping class to each subnode in the tree. - * @param {!Node} node - * @param {string} oldScope - */ - domRemoveScope(node, oldScope) { - const fn = (node) => { - this.element(node, oldScope || '', true); - }; - this._transformDom(node, fn); - } - - /** - * @param {?} element - * @param {?} styleRules - * @param {?=} callback - * @param {string=} cssBuild - * @param {string=} cssText - * @return {string} - */ - elementStyles(element, styleRules, callback, cssBuild = '', cssText = '') { - // no need to shim selectors if settings.useNativeShadow, also - // a shady css build will already have transformed selectors - // NOTE: This method may be called as part of static or property shimming. - // When there is a targeted build it will not be called for static shimming, - // but when the property shim is used it is called and should opt out of - // static shimming work when a proper build exists. - if (cssText === '') { - if (nativeShadow || cssBuild === 'shady') { - cssText = StyleUtil.toCssText(styleRules, callback); - } else { - let {is, typeExtension} = StyleUtil.getIsExtends(element); - cssText = this.css(styleRules, is, typeExtension, callback) + '\n\n'; - } - } - return cssText.trim(); - } - - // Given a string of cssText and a scoping string (scope), returns - // a string of scoped css where each selector is transformed to include - // a class created from the scope. ShadowDOM selectors are also transformed - // (e.g. :host) to use the scoping selector. - css(rules, scope, ext, callback) { - let hostScope = this._calcHostScope(scope, ext); - scope = this._calcElementScope(scope); - let self = this; - return StyleUtil.toCssText(rules, function(/** StyleNode */rule) { - if (!rule.isScoped) { - self.rule(rule, scope, hostScope); - rule.isScoped = true; - } - if (callback) { - callback(rule, scope, hostScope); - } - }); - } - - _calcElementScope(scope) { - if (scope) { - return CSS_CLASS_PREFIX + scope; - } else { - return ''; - } - } - - _calcHostScope(scope, ext) { - return ext ? `[is=${scope}]` : scope; - } - - rule(rule, scope, hostScope) { - this._transformRule(rule, this._transformComplexSelector, - scope, hostScope); - } - - /** - * transforms a css rule to a scoped rule. - * - * @param {StyleNode} rule - * @param {Function} transformer - * @param {string=} scope - * @param {string=} hostScope - */ - _transformRule(rule, transformer, scope, hostScope) { - // NOTE: save transformedSelector for subsequent matching of elements - // against selectors (e.g. when calculating style properties) - rule['selector'] = rule.transformedSelector = - this._transformRuleCss(rule, transformer, scope, hostScope); - } - - /** - * @param {StyleNode} rule - * @param {Function} transformer - * @param {string=} scope - * @param {string=} hostScope - */ - _transformRuleCss(rule, transformer, scope, hostScope) { - let p$ = StyleUtil.splitSelectorList(rule['selector']); - // we want to skip transformation of rules that appear in keyframes, - // because they are keyframe selectors, not element selectors. - if (!StyleUtil.isKeyframesSelector(rule)) { - for (let i=0, l=p$.length, p; (i<l) && (p=p$[i]); i++) { - p$[i] = transformer.call(this, p, scope, hostScope); - } - } - return p$.filter((part) => Boolean(part)).join(COMPLEX_SELECTOR_SEP); - } - - /** - * @param {string} selector - * @return {string} - */ - _twiddleNthPlus(selector) { - return selector.replace(NTH, (m, type, inside) => { - if (inside.indexOf('+') > -1) { - inside = inside.replace(/\+/g, '___'); - } else if (inside.indexOf('___') > -1) { - inside = inside.replace(/___/g, '+'); - } - return `:${type}(${inside})`; - }); - } - - /** - * Preserve `:matches()` selectors by replacing them with MATCHES_REPLACMENT - * and returning an array of `:matches()` selectors. - * Use `_replacesMatchesPseudo` to replace the `:matches()` parts - * - * @param {string} selector - * @return {{selector: string, matches: !Array<string>}} - */ - _preserveMatchesPseudo(selector) { - /** @type {!Array<string>} */ - const matches = []; - let match; - while ((match = selector.match(MATCHES))) { - const start = match.index; - const end = StyleUtil.findMatchingParen(selector, start); - if (end === -1) { - throw new Error(`${match.input} selector missing ')'`) - } - const part = selector.slice(start, end + 1); - selector = selector.replace(part, MATCHES_REPLACEMENT); - matches.push(part); - } - return {selector, matches}; - } - - /** - * Replace MATCHES_REPLACMENT character with the given set of `:matches()` - * selectors. - * - * @param {string} selector - * @param {!Array<string>} matches - * @return {string} - */ - _replaceMatchesPseudo(selector, matches) { - const parts = selector.split(MATCHES_REPLACEMENT); - return matches.reduce((acc, cur, idx) => acc + cur + parts[idx + 1], parts[0]); - } - -/** - * @param {string} selector - * @param {string} scope - * @param {string=} hostScope - */ - _transformComplexSelector(selector, scope, hostScope) { - let stop = false; - selector = selector.trim(); - // Remove spaces inside of selectors like `:nth-of-type` because it confuses SIMPLE_SELECTOR_SEP - let isNth = NTH.test(selector); - if (isNth) { - selector = selector.replace(NTH, (m, type, inner) => `:${type}(${inner.replace(/\s/g, '')})`) - selector = this._twiddleNthPlus(selector); - } - // Preserve selectors like `:-webkit-any` so that SIMPLE_SELECTOR_SEP does - // not get confused by spaces inside the pseudo selector - const isMatches = MATCHES.test(selector); - /** @type {!Array<string>} */ - let matches; - if (isMatches) { - ({selector, matches} = this._preserveMatchesPseudo(selector)); - } - selector = selector.replace(SLOTTED_START, `${HOST} $1`); - selector = selector.replace(SIMPLE_SELECTOR_SEP, (m, c, s) => { - if (!stop) { - let info = this._transformCompoundSelector(s, c, scope, hostScope); - stop = stop || info.stop; - c = info.combinator; - s = info.value; - } - return c + s; - }); - // replace `:matches()` selectors - if (isMatches) { - selector = this._replaceMatchesPseudo(selector, matches); - } - if (isNth) { - selector = this._twiddleNthPlus(selector); - } - selector = selector.replace(DIR_PAREN, (m, before, dir, after) => - `[dir="${dir}"] ${before}${after}, ${before}[dir="${dir}"]${after}`); - return selector; - } - - _transformCompoundSelector(selector, combinator, scope, hostScope) { - // replace :host with host scoping class - let slottedIndex = selector.indexOf(SLOTTED); - if (selector.indexOf(HOST) >= 0) { - selector = this._transformHostSelector(selector, hostScope); - // replace other selectors with scoping class - } else if (slottedIndex !== 0) { - selector = scope ? this._transformSimpleSelector(selector, scope) : - selector; - } - // mark ::slotted() scope jump to replace with descendant selector + arg - // also ignore left-side combinator - let slotted = false; - if (slottedIndex >= 0) { - combinator = ''; - slotted = true; - } - // process scope jumping selectors up to the scope jump and then stop - let stop; - if (slotted) { - stop = true; - if (slotted) { - // .zonk ::slotted(.foo) -> .zonk.scope > .foo - selector = selector.replace(SLOTTED_PAREN, (m, paren) => ` > ${paren}`); - } - } - return {value: selector, combinator, stop}; - } - - _transformSimpleSelector(selector, scope) { - const attributes = selector.split(/(\[.+?\])/); - - const output = []; - for (let i = 0; i < attributes.length; i++) { - // Do not attempt to transform any attribute selector content - if ((i % 2) === 1) { - output.push(attributes[i]); - } else { - const part = attributes[i]; - - if (!(part === '' && i === attributes.length - 1)) { - let p$ = part.split(PSEUDO_PREFIX); - p$[0] += scope; - output.push(p$.join(PSEUDO_PREFIX)); - } - } - } - - return output.join(''); - } - - // :host(...) -> scopeName... - _transformHostSelector(selector, hostScope) { - let m = selector.match(HOST_PAREN); - let paren = m && m[2].trim() || ''; - if (paren) { - if (!paren[0].match(SIMPLE_SELECTOR_PREFIX)) { - // paren starts with a type selector - let typeSelector = paren.split(SIMPLE_SELECTOR_PREFIX)[0]; - // if the type selector is our hostScope then avoid pre-pending it - if (typeSelector === hostScope) { - return paren; - // otherwise, this selector should not match in this scope so - // output a bogus selector. - } else { - return SELECTOR_NO_MATCH; - } - } else { - // make sure to do a replace here to catch selectors like: - // `:host(.foo)::before` - return selector.replace(HOST_PAREN, function(m, host, paren) { - return hostScope + paren; - }); - } - // if no paren, do a straight :host replacement. - // TODO(sorvell): this should not strictly be necessary but - // it's needed to maintain support for `:host[foo]` type selectors - // which have been improperly used under Shady DOM. This should be - // deprecated. - } else { - return selector.replace(HOST, hostScope); - } - } - - /** - * @param {StyleNode} rule - */ - documentRule(rule) { - // reset selector in case this is redone. - rule['selector'] = rule['parsedSelector']; - this.normalizeRootSelector(rule); - this._transformRule(rule, this._transformDocumentSelector); - } - - /** - * @param {StyleNode} rule - */ - normalizeRootSelector(rule) { - if (rule['selector'] === ROOT) { - rule['selector'] = 'html'; - } - } - -/** - * @param {string} selector - */ - _transformDocumentSelector(selector) { - if (selector.match(HOST)) { - // remove ':host' type selectors in document rules - return ''; - } else if (selector.match(SLOTTED)) { - return this._transformComplexSelector(selector, SCOPE_DOC_SELECTOR) - } else { - return this._transformSimpleSelector(selector.trim(), SCOPE_DOC_SELECTOR); - } - } -} - -const NTH = /:(nth[-\w]+)\(([^)]+)\)/; -const SCOPE_DOC_SELECTOR = `:not(.${SCOPE_NAME})`; -const COMPLEX_SELECTOR_SEP = ','; -const SIMPLE_SELECTOR_SEP = /(^|[\s>+~]+)((?:\[.+?\]|[^\s>+~=[])+)/g; -const SIMPLE_SELECTOR_PREFIX = /[[.:#*]/; -const HOST = ':host'; -const ROOT = ':root'; -const SLOTTED = '::slotted'; -const SLOTTED_START = new RegExp(`^(${SLOTTED})`); -// NOTE: this supports 1 nested () pair for things like -// :host(:not([selected]), more general support requires -// parsing which seems like overkill -const HOST_PAREN = /(:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/; -// similar to HOST_PAREN -const SLOTTED_PAREN = /(?:::slotted)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/; -const DIR_PAREN = /(.*):dir\((?:(ltr|rtl))\)(.*)/; -const CSS_CLASS_PREFIX = '.'; -const PSEUDO_PREFIX = ':'; -const CLASS = 'class'; -const SELECTOR_NO_MATCH = 'should_not_match'; -const MATCHES = /:(?:matches|any|-(?:webkit|moz)-any)/; -const MATCHES_REPLACEMENT = '\u{e000}'; - -export default new StyleTransformer()
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-util.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/style-util.js deleted file mode 100644 index 1e58c5b..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/style-util.js +++ /dev/null
@@ -1,414 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -import {nativeShadow, nativeCssVariables, cssBuild} from './style-settings.js'; -import {parse, stringify, types, StyleNode} from './css-parse.js'; // eslint-disable-line no-unused-vars -import {MEDIA_MATCH} from './common-regex.js'; -import {processUnscopedStyle, isUnscopedStyle} from './unscoped-style-handler.js'; - -/** - * @param {string|StyleNode} rules - * @param {function(StyleNode)=} callback - * @return {string} - */ -export function toCssText (rules, callback) { - if (!rules) { - return ''; - } - if (typeof rules === 'string') { - rules = parse(rules); - } - if (callback) { - forEachRule(rules, callback); - } - return stringify(rules, nativeCssVariables); -} - -/** - * @param {HTMLStyleElement} style - * @return {StyleNode} - */ -export function rulesForStyle(style) { - if (!style['__cssRules'] && style.textContent) { - style['__cssRules'] = parse(style.textContent); - } - return style['__cssRules'] || null; -} - -// Tests if a rule is a keyframes selector, which looks almost exactly -// like a normal selector but is not (it has nothing to do with scoping -// for example). -/** - * @param {StyleNode} rule - * @return {boolean} - */ -export function isKeyframesSelector(rule) { - return Boolean(rule['parent']) && - rule['parent']['type'] === types.KEYFRAMES_RULE; -} - -/** - * @param {StyleNode} node - * @param {Function=} styleRuleCallback - * @param {Function=} keyframesRuleCallback - * @param {boolean=} onlyActiveRules - */ -export function forEachRule(node, styleRuleCallback, keyframesRuleCallback, onlyActiveRules) { - if (!node) { - return; - } - let skipRules = false; - let type = node['type']; - if (onlyActiveRules) { - if (type === types.MEDIA_RULE) { - let matchMedia = node['selector'].match(MEDIA_MATCH); - if (matchMedia) { - // if rule is a non matching @media rule, skip subrules - if (!window.matchMedia(matchMedia[1]).matches) { - skipRules = true; - } - } - } - } - if (type === types.STYLE_RULE) { - styleRuleCallback(node); - } else if (keyframesRuleCallback && - type === types.KEYFRAMES_RULE) { - keyframesRuleCallback(node); - } else if (type === types.MIXIN_RULE) { - skipRules = true; - } - let r$ = node['rules']; - if (r$ && !skipRules) { - for (let i=0, l=r$.length, r; (i<l) && (r=r$[i]); i++) { - forEachRule(r, styleRuleCallback, keyframesRuleCallback, onlyActiveRules); - } - } -} - -// add a string of cssText to the document. -/** - * @param {string} cssText - * @param {string} moniker - * @param {Node} target - * @param {Node} contextNode - * @return {HTMLStyleElement} - */ -export function applyCss(cssText, moniker, target, contextNode) { - let style = createScopeStyle(cssText, moniker); - applyStyle(style, target, contextNode); - return style; -} - -/** - * @param {string} cssText - * @param {string} moniker - * @return {HTMLStyleElement} - */ -export function createScopeStyle(cssText, moniker) { - let style = /** @type {HTMLStyleElement} */(document.createElement('style')); - if (moniker) { - style.setAttribute('scope', moniker); - } - style.textContent = cssText; - return style; -} - -/** - * Track the position of the last added style for placing placeholders - * @type {Node} - */ -let lastHeadApplyNode = null; - -// insert a comment node as a styling position placeholder. -/** - * @param {string} moniker - * @return {!Comment} - */ -export function applyStylePlaceHolder(moniker) { - let placeHolder = document.createComment(' Shady DOM styles for ' + - moniker + ' '); - let after = lastHeadApplyNode ? - lastHeadApplyNode['nextSibling'] : null; - let scope = document.head; - scope.insertBefore(placeHolder, after || scope.firstChild); - lastHeadApplyNode = placeHolder; - return placeHolder; -} - -/** - * @param {HTMLStyleElement} style - * @param {?Node} target - * @param {?Node} contextNode - */ -export function applyStyle(style, target, contextNode) { - target = target || document.head; - let after = (contextNode && contextNode.nextSibling) || - target.firstChild; - target.insertBefore(style, after); - if (!lastHeadApplyNode) { - lastHeadApplyNode = style; - } else { - // only update lastHeadApplyNode if the new style is inserted after the old lastHeadApplyNode - let position = style.compareDocumentPosition(lastHeadApplyNode); - if (position === Node.DOCUMENT_POSITION_PRECEDING) { - lastHeadApplyNode = style; - } - } -} - -/** - * @param {string} buildType - * @return {boolean} - */ -export function isTargetedBuild(buildType) { - return nativeShadow ? buildType === 'shadow' : buildType === 'shady'; -} - -/** - * Walk from text[start] matching parens and - * returns position of the outer end paren - * @param {string} text - * @param {number} start - * @return {number} - */ -export function findMatchingParen(text, start) { - let level = 0; - for (let i=start, l=text.length; i < l; i++) { - if (text[i] === '(') { - level++; - } else if (text[i] === ')') { - if (--level === 0) { - return i; - } - } - } - return -1; -} - -/** - * @param {string} str - * @param {function(string, string, string, string)} callback - */ -export function processVariableAndFallback(str, callback) { - // find 'var(' - let start = str.indexOf('var('); - if (start === -1) { - // no var?, everything is prefix - return callback(str, '', '', ''); - } - //${prefix}var(${inner})${suffix} - let end = findMatchingParen(str, start + 3); - let inner = str.substring(start + 4, end); - let prefix = str.substring(0, start); - // suffix may have other variables - let suffix = processVariableAndFallback(str.substring(end + 1), callback); - let comma = inner.indexOf(','); - // value and fallback args should be trimmed to match in property lookup - if (comma === -1) { - // variable, no fallback - return callback(prefix, inner.trim(), '', suffix); - } - // var(${value},${fallback}) - let value = inner.substring(0, comma).trim(); - let fallback = inner.substring(comma + 1).trim(); - return callback(prefix, value, fallback, suffix); -} - -/** - * @param {Element} element - * @param {string} value - */ -export function setElementClassRaw(element, value) { - // use native setAttribute provided by ShadyDOM when setAttribute is patched - if (nativeShadow) { - element.setAttribute('class', value); - } else { - window['ShadyDOM']['nativeMethods']['setAttribute'].call(element, 'class', value); - } -} - -/** - * @type {function(*):*} - */ -export const wrap = window['ShadyDOM'] && window['ShadyDOM']['wrap'] || ((node) => node); - -/** - * @param {Element | {is: string, extends: string}} element - * @return {{is: string, typeExtension: string}} - */ -export function getIsExtends(element) { - let localName = element['localName']; - let is = '', typeExtension = ''; - /* - NOTE: technically, this can be wrong for certain svg elements - with `-` in the name like `<font-face>` - */ - if (localName) { - if (localName.indexOf('-') > -1) { - is = localName; - } else { - typeExtension = localName; - is = (element.getAttribute && element.getAttribute('is')) || ''; - } - } else { - is = /** @type {?} */(element).is; - typeExtension = /** @type {?} */(element).extends; - } - return {is, typeExtension}; -} - -/** - * @param {Element|DocumentFragment} element - * @return {string} - */ -export function gatherStyleText(element) { - /** @type {!Array<string>} */ - const styleTextParts = []; - const styles = /** @type {!NodeList<!HTMLStyleElement>} */(element.querySelectorAll('style')); - for (let i = 0; i < styles.length; i++) { - const style = styles[i]; - if (isUnscopedStyle(style)) { - if (!nativeShadow) { - processUnscopedStyle(style); - style.parentNode.removeChild(style); - } - } else { - styleTextParts.push(style.textContent); - style.parentNode.removeChild(style); - } - } - return styleTextParts.join('').trim(); -} - -/** - * Split a selector separated by commas into an array in a smart way - * @param {string} selector - * @return {!Array<string>} - */ -export function splitSelectorList(selector) { - const parts = []; - let part = ''; - for (let i = 0; i >= 0 && i < selector.length; i++) { - // A selector with parentheses will be one complete part - if (selector[i] === '(') { - // find the matching paren - const end = findMatchingParen(selector, i); - // push the paren block into the part - part += selector.slice(i, end + 1); - // move the index to after the paren block - i = end; - } else if (selector[i] === ',') { - parts.push(part); - part = ''; - } else { - part += selector[i]; - } - } - // catch any pieces after the last comma - if (part) { - parts.push(part); - } - return parts; -} - -const CSS_BUILD_ATTR = 'css-build'; - -/** - * Return the polymer-css-build "build type" applied to this element - * - * @param {!HTMLElement} element - * @return {string} Can be "", "shady", or "shadow" - */ -export function getCssBuild(element) { - if (cssBuild !== undefined) { - return /** @type {string} */(cssBuild); - } - if (element.__cssBuild === undefined) { - // try attribute first, as it is the common case - const attrValue = element.getAttribute(CSS_BUILD_ATTR); - if (attrValue) { - element.__cssBuild = attrValue; - } else { - const buildComment = getBuildComment(element); - if (buildComment !== '') { - // remove build comment so it is not needlessly copied into every element instance - removeBuildComment(element); - } - element.__cssBuild = buildComment; - } - } - return element.__cssBuild || ''; -} - -/** - * Check if the given element, either a <template> or <style>, has been processed - * by polymer-css-build. - * - * If so, then we can make a number of optimizations: - * - polymer-css-build will decompose mixins into individual CSS Custom Properties, - * so the ApplyShim can be skipped entirely. - * - Under native ShadowDOM, the style text can just be copied into each instance - * without modification - * - If the build is "shady" and ShadyDOM is in use, the styling does not need - * scoping beyond the shimming of CSS Custom Properties - * - * @param {!HTMLElement} element - * @return {boolean} - */ -export function elementHasBuiltCss(element) { - return getCssBuild(element) !== ''; -} - -/** - * For templates made with tagged template literals, polymer-css-build will - * insert a comment of the form `<!--css-build:shadow-->` - * - * @param {!HTMLElement} element - * @return {string} - */ -export function getBuildComment(element) { - const buildComment = element.localName === 'template' ? - /** @type {!HTMLTemplateElement} */ (element).content.firstChild : - element.firstChild; - if (buildComment instanceof Comment) { - const commentParts = buildComment.textContent.trim().split(':'); - if (commentParts[0] === CSS_BUILD_ATTR) { - return commentParts[1]; - } - } - return ''; -} - -/** - * Check if the css build status is optimal, and do no unneeded work. - * - * @param {string=} cssBuild CSS build status - * @return {boolean} css build is optimal or not - */ -export function isOptimalCssBuild(cssBuild = '') { - // CSS custom property shim always requires work - if (cssBuild === '' || !nativeCssVariables) { - return false; - } - return nativeShadow ? cssBuild === 'shadow' : cssBuild === 'shady'; -} - -/** - * @param {!HTMLElement} element - */ -function removeBuildComment(element) { - const buildComment = element.localName === 'template' ? - /** @type {!HTMLTemplateElement} */ (element).content.firstChild : - element.firstChild; - buildComment.parentNode.removeChild(buildComment); -}
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/template-map.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/template-map.js deleted file mode 100644 index d3eb840..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/template-map.js +++ /dev/null
@@ -1,17 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -/** - * @const {!Object<string, !HTMLTemplateElement>} - */ -const templateMap = {}; -export default templateMap;
diff --git a/third_party/polymer/v3_0/components-chromium/shadycss/src/unscoped-style-handler.js b/third_party/polymer/v3_0/components-chromium/shadycss/src/unscoped-style-handler.js deleted file mode 100644 index 95e994f..0000000 --- a/third_party/polymer/v3_0/components-chromium/shadycss/src/unscoped-style-handler.js +++ /dev/null
@@ -1,40 +0,0 @@ -/** -@license -Copyright (c) 2017 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt -*/ - -'use strict'; - -/** @type {!Set<string>} */ -const styleTextSet = new Set(); - -export const scopingAttribute = 'shady-unscoped'; - -/** - * Add a specifically-marked style to the document directly, and only one copy of that style. - * - * @param {!HTMLStyleElement} style - * @return {undefined} - */ -export function processUnscopedStyle(style) { - const text = style.textContent; - if (!styleTextSet.has(text)) { - styleTextSet.add(text); - const newStyle = style.cloneNode(true); - document.head.appendChild(newStyle); - } -} - -/** - * Check if a style is supposed to be unscoped - * @param {!HTMLStyleElement} style - * @return {boolean} true if the style has the unscoping attribute - */ -export function isUnscopedStyle(style) { - return style.hasAttribute(scopingAttribute); -} \ No newline at end of file
diff --git a/third_party/polymer/v3_0/minify_polymer.py b/third_party/polymer/v3_0/minify_polymer.py new file mode 100644 index 0000000..462b0bc9 --- /dev/null +++ b/third_party/polymer/v3_0/minify_polymer.py
@@ -0,0 +1,75 @@ +# Copyrigh 2019 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. + +"""Minifies Polymer 3, since it does not come already minified from NPM.""" + +import os +import shutil +import sys +import tempfile + +_HERE_PATH = os.path.dirname(__file__) +_SRC_PATH = os.path.normpath(os.path.join(_HERE_PATH, '..', '..', '..')) +sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'node')) +import node +import node_modules + + +def main(): + polymer_dir = os.path.join(_HERE_PATH, 'components-chromium', 'polymer') + + # Final JS bundle. + polymer_js = os.path.join(polymer_dir, 'polymer_bundled.min.js') + + # Copy the top-level Polymer file that holds all dependencies. This file is + # not distributed via NPM, it only exists within third_party/polymer + # repository. + shutil.copy( + os.path.join(polymer_dir, '..', '..', 'polymer.js'), polymer_dir); + + # Move the entire checkout to a temp location. + tmp_dir = os.path.join(_HERE_PATH, 'components-chromium', 'polymer_temp') + if os.path.exists(tmp_dir): + shutil.rmtree(tmp_dir) + shutil.move(polymer_dir, tmp_dir) + + tmp_out_dir = os.path.join(tmp_dir, 'out') + os.makedirs(tmp_out_dir) + + try: + # Combine everything to a single JS bundle file. + bundled_js = os.path.join(tmp_out_dir, 'polymer_bundled.js') + path_to_rollup = os.path.join('node_modules', 'rollup', 'bin', 'rollup'); + + node.RunNode([ + path_to_rollup, + # See https://github.com/rollup/rollup/issues/1955 + '--silent', + '--format', 'esm', + '--input', os.path.join(tmp_dir, 'polymer.js'), + '--file', bundled_js, + ]) + + # Minify the JS bundle. + minified_js = os.path.join(tmp_out_dir, 'polymer_bundled.min.js') + node.RunNode([ + node_modules.PathToUglify(), bundled_js, + # TODO(dpapad): Figure out a way to deduplicate LICENSE headers. + #'--comments', '"/Copyright|license|LICENSE/"', + '--output', minified_js]) + + # Copy generated JS bundle back to the original location. + os.makedirs(polymer_dir) + shutil.move(minified_js, polymer_js) + + # Copy a few more files. + shutil.move(os.path.join(tmp_dir, 'LICENSE.txt'), polymer_dir) + finally: + # Delete component-chromium/shadycss since it ends up in the bundle. + shutil.rmtree(os.path.join(_HERE_PATH, 'components-chromium', 'shadycss')) + shutil.rmtree(tmp_dir) + + +if __name__ == '__main__': + main()
diff --git a/third_party/polymer/v3_0/package-lock.json b/third_party/polymer/v3_0/package-lock.json index 8b8fa5a..102529e 100644 --- a/third_party/polymer/v3_0/package-lock.json +++ b/third_party/polymer/v3_0/package-lock.json
@@ -353,10 +353,35 @@ "@webcomponents/shadycss": "1.9.1" } }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + }, + "@types/node": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.3.tgz", + "integrity": "sha512-zkOxCS/fA+3SsdA+9Yun0iANxzhQRiNwTvJSr6N95JhuJ/x27z9G2URx1Jpt3zYFfCGUXZGL5UDxt5eyLE7wgw==" + }, "@webcomponents/shadycss": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@webcomponents/shadycss/-/shadycss-1.9.1.tgz", "integrity": "sha512-IaZOnWOKXHghqk/WfPNDRIgDBi3RsVPY2IFAw6tYiL9UBGvQRy5R6uC+Fk7qTZsReTJ0xh5MTT8yAcb3MUR4mQ==" + }, + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==" + }, + "rollup": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.12.4.tgz", + "integrity": "sha512-sHg0F05oTMJzM592MWU8irsPx8LIFMKSCnEkcp6vp/gnj+oJ9GJEBW9hl8jUqy2L6Q2uUxFzPgvoExLbfuSODA==", + "requires": { + "@types/estree": "0.0.39", + "@types/node": "12.0.3", + "acorn": "6.1.1" + } } } }
diff --git a/third_party/polymer/v3_0/package.json b/third_party/polymer/v3_0/package.json index 430cfa5..866685a 100644 --- a/third_party/polymer/v3_0/package.json +++ b/third_party/polymer/v3_0/package.json
@@ -3,41 +3,42 @@ "version": "1.0.0", "author": "dpapad@chromium.org", "dependencies": { - "@polymer/font-roboto": "^3.0.2", - "@polymer/iron-a11y-announcer": "^3.0.2", - "@polymer/iron-a11y-keys": "^3.0.1", - "@polymer/iron-a11y-keys-behavior": "^3.0.1", - "@polymer/iron-behaviors": "^3.0.1", - "@polymer/iron-collapse": "^3.0.1", - "@polymer/iron-dropdown": "^3.0.1", - "@polymer/iron-fit-behavior": "^3.0.1", - "@polymer/iron-flex-layout": "^3.0.1", - "@polymer/iron-icon": "^3.0.1", - "@polymer/iron-iconset-svg": "^3.0.1", - "@polymer/iron-input": "^3.0.1", - "@polymer/iron-list": "^3.0.2", - "@polymer/iron-location": "^3.0.1", - "@polymer/iron-media-query": "^3.0.1", - "@polymer/iron-meta": "^3.0.1", - "@polymer/iron-overlay-behavior": "^3.0.2", - "@polymer/iron-pages": "^3.0.1", - "@polymer/iron-range-behavior": "^3.0.1", - "@polymer/iron-resizable-behavior": "^3.0.1", - "@polymer/iron-scroll-target-behavior": "^3.0.1", - "@polymer/iron-scroll-threshold": "^3.0.1", - "@polymer/iron-selector": "^3.0.1", - "@polymer/iron-test-helpers": "^3.0.1", - "@polymer/iron-validatable-behavior": "^3.0.1", - "@polymer/neon-animation": "^3.0.1", - "@polymer/paper-behaviors": "^3.0.1", - "@polymer/paper-button": "^3.0.1", - "@polymer/paper-input": "^3.0.2", - "@polymer/paper-progress": "^3.0.1", - "@polymer/paper-ripple": "^3.0.1", - "@polymer/paper-spinner": "^3.0.2", - "@polymer/paper-styles": "^3.0.1", - "@polymer/paper-tooltip": "^3.0.1", - "@polymer/polymer": "^3.2.0", - "@webcomponents/shadycss": "^1.9.1" + "@polymer/font-roboto": "3.0.2", + "@polymer/iron-a11y-announcer": "3.0.2", + "@polymer/iron-a11y-keys": "3.0.1", + "@polymer/iron-a11y-keys-behavior": "3.0.1", + "@polymer/iron-behaviors": "3.0.1", + "@polymer/iron-collapse": "3.0.1", + "@polymer/iron-dropdown": "3.0.1", + "@polymer/iron-fit-behavior": "3.0.1", + "@polymer/iron-flex-layout": "3.0.1", + "@polymer/iron-icon": "3.0.1", + "@polymer/iron-iconset-svg": "3.0.1", + "@polymer/iron-input": "3.0.1", + "@polymer/iron-list": "3.0.2", + "@polymer/iron-location": "3.0.1", + "@polymer/iron-media-query": "3.0.1", + "@polymer/iron-meta": "3.0.1", + "@polymer/iron-overlay-behavior": "3.0.2", + "@polymer/iron-pages": "3.0.1", + "@polymer/iron-range-behavior": "3.0.1", + "@polymer/iron-resizable-behavior": "3.0.1", + "@polymer/iron-scroll-target-behavior": "3.0.1", + "@polymer/iron-scroll-threshold": "3.0.1", + "@polymer/iron-selector": "3.0.1", + "@polymer/iron-test-helpers": "3.0.1", + "@polymer/iron-validatable-behavior": "3.0.1", + "@polymer/neon-animation": "3.0.1", + "@polymer/paper-behaviors": "3.0.1", + "@polymer/paper-button": "3.0.1", + "@polymer/paper-input": "3.0.2", + "@polymer/paper-progress": "3.0.1", + "@polymer/paper-ripple": "3.0.1", + "@polymer/paper-spinner": "3.0.2", + "@polymer/paper-styles": "3.0.1", + "@polymer/paper-tooltip": "3.0.1", + "@polymer/polymer": "3.2.0", + "@webcomponents/shadycss": "1.9.1", + "rollup": "1.12.4" } }
diff --git a/third_party/polymer/v3_0/polymer.js b/third_party/polymer/v3_0/polymer.js new file mode 100644 index 0000000..4bf68e2 --- /dev/null +++ b/third_party/polymer/v3_0/polymer.js
@@ -0,0 +1,25 @@ +/** +@license +Copyright (c) 2019 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +*/ + +export {animationFrame, idlePeriod, microTask} from './lib/utils/async.js'; +import * as gestures from './lib/utils/gestures.js'; +export {gestures}; +export {Base} from './polymer-legacy.js'; +export {dashToCamelCase} from './lib/utils/case-map.js'; +export {Debouncer, enqueueDebouncer} from './lib/utils/debounce.js'; +export {dom, flush} from './lib/legacy/polymer.dom.js'; +export {html} from './lib/utils/html-tag.js'; +export {matches, translate} from './lib/utils/path.js'; +export {OptionalMutableDataBehavior} from './lib/legacy/mutable-data-behavior.js'; +export {Polymer} from './lib/legacy/polymer-fn.js'; +export {PolymerElement} from './polymer-element.js'; +export {TemplateInstanceBase} from './lib/utils/templatize.js'; +export {Templatizer} from './lib/legacy/templatizer-behavior.js'; +export {useShadow} from './lib/utils/settings.js';
diff --git a/third_party/polymer/v3_0/reproduce.sh b/third_party/polymer/v3_0/reproduce.sh index bbf9fd9..74746f2 100755 --- a/third_party/polymer/v3_0/reproduce.sh +++ b/third_party/polymer/v3_0/reproduce.sh
@@ -48,6 +48,14 @@ # Apply additional chrome specific patches. patch -p1 --forward -r - < chromium.patch +echo 'Minifying Polymer 3, since it comes non-minified from NPM.' +python minify_polymer.py + +echo 'Updating paper/iron elements to point to the minified file.' +# Replace all paths that point to within polymer/ to point to the bundle. +find components-chromium/ -name '*.js' -exec sed -i \ + 's/\/polymer\/[a-zA-Z\/\.-]\+/\/polymer\/polymer_bundled.min.js/' {} + + new=$(git status --porcelain components-chromium | grep '^??' | \ cut -d' ' -f2 | egrep '\.(js|css)$' || true)
diff --git a/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/core/renderer.js b/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/core/renderer.js index 04ebf2d..c4d7c34 100644 --- a/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/core/renderer.js +++ b/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/core/renderer.js
@@ -557,7 +557,7 @@ if (buffer._buffer) { let gl = this._gl; gl.bindBuffer(buffer._target, buffer._buffer); - if (offset == 0 && buffer._length == data.byteLength) { + if (offset == 0 && buffer._length <= data.byteLength) { gl.bufferData(buffer._target, data, buffer._usage); } else { gl.bufferSubData(buffer._target, offset, data);
diff --git a/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/nodes/plane-node.js b/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/nodes/plane-node.js index 95d59e4..ad28946 100644 --- a/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/nodes/plane-node.js +++ b/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/nodes/plane-node.js
@@ -18,8 +18,9 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import {Material, RENDER_ORDER} from '../core/material.js'; +import {Primitive, PrimitiveAttribute} from '../core/primitive.js'; import {Node} from '../core/node.js'; -import {GeometryBuilderBase} from '../geometry/primitive-stream.js'; +import {vec3} from '../math/gl-matrix.js'; const GL = WebGLRenderingContext; // For enums @@ -45,7 +46,6 @@ get vertexSource() { return ` attribute vec3 POSITION; - attribute vec3 NORMAL; varying vec3 vLight; @@ -54,7 +54,7 @@ const vec3 lightColor = vec3(0.75, 0.75, 0.75); vec4 vertex_main(mat4 proj, mat4 view, mat4 model) { - vec3 normalRotated = vec3(model * vec4(NORMAL, 0.0)); + vec3 normalRotated = vec3(model * vec4(0.0, 0.0, 1.0, 0.0)); float lightFactor = max(dot(normalize(lightDir), normalRotated), 0.0); vLight = ambientColor + (lightColor * lightFactor); return proj * view * model * vec4(POSITION, 1.0); @@ -87,28 +87,85 @@ this.baseColor = options.baseColor; this.polygon = options.polygon; + // ring buffer containing last 3 plane primitives (meshes) + this.primitives = [null, null, null]; + this.primitiveIndex = -1; + this._material = new PlaneMaterial({baseColor : options.baseColor}); this._renderer = null; } createPlanePrimitive(polygon) { - // TODO: create new builder class for planes - let planeBuilder = new GeometryBuilderBase(); + let vertices = []; + let indices = []; + let min = null; + let max = null; - planeBuilder.primitiveStream.startGeometry(); - let numVertices = polygon.length; - let firstVertex = planeBuilder.primitiveStream.nextVertexIndex; + // first, collect all polygon vertices polygon.forEach(vertex => { - planeBuilder.primitiveStream.pushVertex(vertex.x, vertex.y, vertex.z); + vertices.push(vertex.x, vertex.y, vertex.z); + + if(min) { + min[0] = Math.min(min[0], vertex.x); + min[1] = Math.min(min[1], vertex.y); + min[2] = Math.min(min[2], vertex.z); + } else { + min = vec3.fromValues(vertex.x, vertex.y, vertex.z); + } + + if(max) { + max[0] = Math.min(min[0], vertex.x); + max[1] = Math.min(max[1], vertex.y); + max[2] = Math.min(max[2], vertex.z); + } else { + max = vec3.fromValues(vertex.x, vertex.y, vertex.z); + } }); - for(let i = 0; i < numVertices - 2; i++) { - planeBuilder.primitiveStream.pushTriangle(firstVertex, firstVertex + i + 1, firstVertex + i + 2); + // then indices + for(let i = 0; i < polygon.length - 2; i++) { + indices.push(0, i + 1, i + 2); } - planeBuilder.primitiveStream.endGeometry(); - return planeBuilder.finishPrimitive(this._renderer); + let newPrimitiveIndex = (this.primitiveIndex + 1) % this.primitives.length; + if(this.primitives[newPrimitiveIndex]) { + // update + let oldPrimitive = this.primitives[newPrimitiveIndex]; + + this._renderer.updateRenderBuffer(oldPrimitive.attributes[0].buffer, new Float32Array(vertices)); + this._renderer.updateRenderBuffer(oldPrimitive.indexBuffer, new Uint16Array(indices)); + + // attribs are still set, no need to re-set them + + // index buffer is still set, no need to re-set it + oldPrimitive.setBounds(min, max); + oldPrimitive.elementCount = indices.length; + } else { + // add + let vertexBuffer = this._renderer.createRenderBuffer( + GL.ARRAY_BUFFER, new Float32Array(vertices)); + let indexBuffer = this._renderer.createRenderBuffer( + GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices)); + + let position_attribute = new PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 12, 0); + + let newPrimitive = new Primitive([position_attribute], indices.length); + newPrimitive.setIndexBuffer(indexBuffer); + newPrimitive.setBounds(min, max); + + this.primitives[newPrimitiveIndex] = newPrimitive; + } + + this.primitiveIndex = newPrimitiveIndex; + } + + get primitive() { + if(!this.primitives[this.primitiveIndex]) { + throw new Error(`Primitive is not set! Call createPlanePrimitive first!`); + } + + return this.primitives[this.primitiveIndex]; } onRendererChanged(renderer) { @@ -117,8 +174,9 @@ this._renderer = renderer; - this.planeNode = this._renderer.createRenderPrimitive( - this.createPlanePrimitive(this.polygon), this._material); + this.createPlanePrimitive(this.polygon); + + this.planeNode = this._renderer.createRenderPrimitive(this.primitive, this._material); this.addRenderPrimitive(this.planeNode); this.polygon = null; @@ -130,9 +188,16 @@ if(this.polygon) throw new Error(`Polygon is set on a plane where it shouldn't be!`); - let updatedPrimitive = this.createPlanePrimitive(polygon); + this.createPlanePrimitive(polygon); - this.planeNode.setPrimitive(updatedPrimitive); + // eagerly clean up render primitive's VAO + if(this.planeNode._vao) { + this._renderer._vaoExt.deleteVertexArrayOES(this.planeNode._vao); + this.planeNode._vao = null; + } + + this.planeNode.setPrimitive(this.primitive); + return this.planeNode.waitForComplete(); } }
diff --git a/tools/binary_size/diagnose_bloat.py b/tools/binary_size/diagnose_bloat.py index 7727039..c4cb3a3cb 100755 --- a/tools/binary_size/diagnose_bloat.py +++ b/tools/binary_size/diagnose_bloat.py
@@ -268,14 +268,11 @@ if self.IsLinux(): return 'chrome' if 'monochrome' in self.target or 'trichrome' in self.target: - ret = 'lib.unstripped/libmonochrome_base.so' + ret = 'lib.unstripped/libmonochrome.so' elif 'webview' in self.target: ret = 'lib.unstripped/libwebviewchromium.so' else: - ret = 'lib.unstripped/libchrome_base.so' - # Maintain support for measuring non-bundle apks. - if not self.is_bundle: - ret = ret.replace('_base', '') + ret = 'lib.unstripped/libchrome.so' return ret @property
diff --git a/tools/chrome_proxy/webdriver/https_previews.py b/tools/chrome_proxy/webdriver/https_previews.py index 5555ed4b..167ae76f 100644 --- a/tools/chrome_proxy/webdriver/https_previews.py +++ b/tools/chrome_proxy/webdriver/https_previews.py
@@ -72,9 +72,9 @@ self.assertRegexpMatches(response.url, LITEPAGES_REGEXP) self.assertEqual(200, response.status) lite_page_responses += 1 - if 'image/' in content_type: - self.assertRegexpMatches(response.url, LITEPAGES_REGEXP) - self.assertEqual(200, response.status) + if ('image/' in content_type + and re.match(LITEPAGES_REGEXP, response.url) + and 200 == response.status): image_responses += 1 self.assertEqual(1, lite_page_responses)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 07299ed..10b5908 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -3293,6 +3293,7 @@ <int value="2" label="Drag and drop"/> <int value="3" label="Double click"/> <int value="4" label="Pause"/> + <int value="5" label="Scroll"/> </enum> <enum name="AutocompleteActionPredictorAction"> @@ -25019,6 +25020,7 @@ </summary> <int value="0" label="All frames have distinct SiteInstances"/> <int value="1" label="Some frames have the same SiteInstance"/> + <int value="2" label="Only one frame exists"/> </enum> <enum name="FrameVisibility"> @@ -33301,6 +33303,7 @@ <int value="-1811394154" label="disable-webrtc-hw-vp8-encoding"/> <int value="-1810294310" label="AndroidPaymentApps:enabled"/> <int value="-1809891158" label="WebAuthenticationCable:enabled"/> + <int value="-1809865209" label="KernelnextVMs:enabled"/> <int value="-1809835803" label="LayoutNG:disabled"/> <int value="-1808576075" label="SystemTrayUnified:enabled"/> <int value="-1808477331" label="MidiManagerCros:disabled"/> @@ -34517,6 +34520,7 @@ <int value="-11260186" label="enable-offline-pages-as-saved-pages"/> <int value="-10709540" label="OmniboxUIExperimentHideSuggestionUrlScheme:enabled"/> + <int value="-9599490" label="KernelnextVMs:disabled"/> <int value="-5052940" label="enable-simplified-fullscreen"/> <int value="-3093629" label="DcheckIsFatal:disabled"/> <int value="-2953333" label="AndroidHistoryManager:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 5e716c00..2162b97 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -4025,6 +4025,17 @@ </summary> </histogram> +<histogram name="Apps.AppList.ZeroStateSearchResultRemovalDecision" + enum="AppListZeroStateResultRemovalConfirmation" expires_after="2019-12-31"> + <owner>jennyz@chromium.org</owner> + <owner>newcomer@chromium.org</owner> + <summary> + The decision of the user whether to remove a zero state search result. This + is gathered per click of a remove or cancel button of a search result + removal confirmation dialog. + </summary> +</histogram> + <histogram name="Apps.AppList.ZeroStateSearchResultUserActionType" enum="AppListZeroStateSearchResultUserActionType" expires_after="2019-12-31"> @@ -4038,6 +4049,10 @@ <histogram name="Apps.AppList.ZeroStateSearchResutRemovalDecision" enum="AppListZeroStateResultRemovalConfirmation" expires_after="2019-12-31"> + <obsolete> + Deprecated June 2019, replaced by + Apps.AppList.ZeroStateSearchResultRemovalDecision. + </obsolete> <owner>jennyz@chromium.org</owner> <owner>newcomer@chromium.org</owner> <summary> @@ -12578,7 +12593,7 @@ </histogram> <histogram name="Blink.Fonts.HarfBuzzFaceZeroCopyAccess" enum="BooleanSuccess" - expires_after="M77"> + expires_after="M82"> <owner>drott@chromium.org</owner> <summary> Counts success or failure of attempting to access font tables using the zero @@ -12597,7 +12612,7 @@ </histogram> <histogram name="Blink.Fonts.VariableFontsRatio" - enum="WebFontInstantiationResult" expires_after="M77"> + enum="WebFontInstantiationResult" expires_after="M82"> <owner>drott@chromium.org</owner> <summary> Tracks adoption ratio of variable fonts compared to conventional (in the @@ -12607,7 +12622,7 @@ </histogram> <histogram name="Blink.Fonts.WindowsUniqueLocalFontInstantiationResult" - enum="WindowsUniqueLocalFontInstantiationResult" expires_after="M78"> + enum="WindowsUniqueLocalFontInstantiationResult" expires_after="M82"> <owner>drott@chromium.org</owner> <summary> Tracks whether a locally uniquely matched font can be instantiated inside @@ -26123,7 +26138,7 @@ </summary> </histogram> -<histogram name="DirectWrite.Fonts.Gfx.InitializeLoopCount" expires_after="M77"> +<histogram name="DirectWrite.Fonts.Gfx.InitializeLoopCount" expires_after="M82"> <owner>drott@chromium.org</owner> <owner>etienneb@chromium.org</owner> <summary> @@ -26175,7 +26190,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.CreateFontFaceResult" enum="Hresult" - expires_after="M77"> + expires_after="M82"> <owner>drott@chromium.org</owner> <summary>Records the error returned from CreateFontFace.</summary> </histogram> @@ -26186,7 +26201,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.FallbackResult" - enum="DirectWriteFontFallbackResult" expires_after="M77"> + enum="DirectWriteFontFallbackResult" expires_after="M82"> <owner>drott@chromium.org</owner> <summary> Measures the effectiveness of the font fallback proxy. This tracks if we @@ -26196,7 +26211,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.FamilyCount" units="families" - expires_after="M77"> + expires_after="M82"> <owner>drott@chromium.org</owner> <summary> The number of font families as seen by the font proxy in the renderer. @@ -26212,13 +26227,13 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.GetSystemFontCollectionResult" - enum="Hresult" expires_after="M77"> + enum="Hresult" expires_after="M82"> <owner>drott@chromium.org</owner> <summary>Records the error returned from GetSystemFontCollection.</summary> </histogram> <histogram name="DirectWrite.Fonts.Proxy.IndexingSpeed" - units="font files per second" expires_after="M78"> + units="font files per second" expires_after="M82"> <owner>drott@chromium.org</owner> <summary> Indexing speed in font files per second while building the local font unique @@ -26228,7 +26243,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.LastResortFontCount" units="fonts" - expires_after="M77"> + expires_after="M82"> <owner>drott@chromium.org</owner> <summary> The number of last resort fallback fonts found on the system as seen by the @@ -26237,7 +26252,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.LastResortFontFileCount" units="files" - expires_after="M77"> + expires_after="M82"> <owner>drott@chromium.org</owner> <summary> The number of font files found for a last resort fallback font. @@ -26245,7 +26260,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.LoaderType" - enum="DirectWriteFontLoaderType" expires_after="M77"> + enum="DirectWriteFontLoaderType" expires_after="M82"> <owner>drott@chromium.org</owner> <summary> The codepath that was used to load a font family. This is logged in the @@ -26255,7 +26270,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.LoadFamilyResult" - enum="DirectWriteLoadFamilyResult" expires_after="M77"> + enum="DirectWriteLoadFamilyResult" expires_after="M82"> <owner>drott@chromium.org</owner> <summary> The outcome of attempting to load a font family in the renderer (success vs @@ -26265,7 +26280,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.LoadFamilyTime" units="ms" - expires_after="M77"> + expires_after="M82"> <owner>drott@chromium.org</owner> <summary> The time taken to load a font family, excluding glyph data. This is logged @@ -26274,7 +26289,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.LookupTableBuildTime" units="ms" - expires_after="M78"> + expires_after="M82"> <owner>drott@chromium.org</owner> <summary> Time it takes to build the font unique name lookup table. Recorded at the @@ -26284,7 +26299,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.LookupTableDiskCacheHit" - units="BooleanSuccess" expires_after="M78"> + units="BooleanSuccess" expires_after="M82"> <owner>drott@chromium.org</owner> <summary> Measures whether the font table lookup structure was loaded from disk cache @@ -26295,7 +26310,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.LookupTablePersistSuccess" - units="BooleanSuccess" expires_after="M78"> + units="BooleanSuccess" expires_after="M82"> <owner>drott@chromium.org</owner> <summary> Measures whether the font table lookup structure was successfully persisted @@ -26305,7 +26320,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.LookupTableReadyTime" units="ms" - expires_after="M78"> + expires_after="M82"> <owner>drott@chromium.org</owner> <summary> Time it takes from scheduling the preparation of the font lookup table until @@ -26317,7 +26332,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.LookupTableSize" units="KB" - expires_after="M78"> + expires_after="M82"> <owner>drott@chromium.org</owner> <summary> Size of the font unique name lookup table in kilobytes. Recorded at the @@ -26335,7 +26350,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.NumFontFiles" units="font files" - expires_after="M78"> + expires_after="M82"> <owner>drott@chromium.org</owner> <summary> Number of font files which were encountered during scanning for locally @@ -26346,7 +26361,7 @@ </histogram> <histogram name="DirectWrite.Fonts.Proxy.TableBuildTimedOut" - units="BooleanTimedout" expires_after="M78"> + units="BooleanTimedout" expires_after="M82"> <owner>drott@chromium.org</owner> <summary> Shows whether the font lookup table construction timed out. Recorded when @@ -31814,6 +31829,17 @@ </summary> </histogram> +<histogram name="Enterprise.Policies.IgnoredByPolicyGroup" + enum="EnterprisePolicies" expires_after="M78"> + <owner>ydago@chromium.org</owner> + <summary> + A set of enterprise policy rules that are ignored because they do not share + the highest priority from their policy atomic group. This is recorded at + startup, then every 24 hours. If chrome is not running at the 24 hours mark, + this will be recorded at the next startup. + </summary> +</histogram> + <histogram name="Enterprise.Policy" enum="EnterprisePolicyType" expires_after="2019-03-15"> <obsolete> @@ -44984,6 +45010,9 @@ </histogram> <histogram name="GPU.OopRaster.PaintOpSerializationSize" units="bytes"> + <obsolete> + Deprecated 06/2019. + </obsolete> <owner>khushalsagar@chromium.org</owner> <summary> This records the number of bytes required to serialize a paint op for OOP @@ -55228,7 +55257,8 @@ </summary> </histogram> -<histogram name="Media.IsStreaming" enum="Boolean" expires_after="M77"> +<histogram name="Media.IsStreaming" enum="Boolean" expires_after="2020-06-03"> + <owner>dalecurtis@chromium.org</owner> <owner>sandersd@chromium.org</owner> <summary> Whether the WMPI data source is streaming (does not support range requests). @@ -92553,6 +92583,23 @@ <histogram name="PerformanceManager.FrameSiteInstanceProcessRelationship.ByProcess" enum="FrameSiteInstanceProcessRelationship" expires_after="2019-09-30"> + <obsolete> + Deprecated 2019-06-04 in favour of variant 2. + </obsolete> + <owner>chrisha@chromium.org</owner> + <owner>catan-team@chromium.org</owner> + <summary> + Tracks the types of frames that are being hosted by a process over its + entire lifetime. An entry in the + "AllFramesHaveDistinctSiteInstances" means that the process only + ever hosted frames with distinct site instances over its entire lifetime. + Recorded on state changes and every 5 minutes. + </summary> +</histogram> + +<histogram + name="PerformanceManager.FrameSiteInstanceProcessRelationship.ByProcess2" + enum="FrameSiteInstanceProcessRelationship" expires_after="2019-09-30"> <owner>chrisha@chromium.org</owner> <owner>catan-team@chromium.org</owner> <summary> @@ -92567,6 +92614,21 @@ <histogram name="PerformanceManager.FrameSiteInstanceProcessRelationship.ByTime" enum="FrameSiteInstanceProcessRelationship" expires_after="2019-09-30"> + <obsolete> + Deprecated 2019-06-04 in favour of variant 2. + </obsolete> + <owner>chrisha@chromium.org</owner> + <owner>catan-team@chromium.org</owner> + <summary> + Tracks the types of frames that are being hosted by a process, aggregated + over time. Each entry in each bucket corresponds to a process being in that + state for one second. Recorded on state changes and every 5 minutes. + </summary> +</histogram> + +<histogram + name="PerformanceManager.FrameSiteInstanceProcessRelationship.ByTime2" + enum="FrameSiteInstanceProcessRelationship" expires_after="2019-09-30"> <owner>chrisha@chromium.org</owner> <owner>catan-team@chromium.org</owner> <summary> @@ -100149,7 +100211,7 @@ </histogram> <histogram name="Previews.ServerLitePage.URLLoader.Attempted" - enum="BooleanAttempted" expires_after="2019-08-30"> + enum="BooleanAttempted" expires_after="2020-08-30"> <owner>ryansturm@chromium.org</owner> <summary> Whether or not the server lite page preview was attempted during a @@ -147632,6 +147694,16 @@ name="Autofill.StrikeDatabase.StrikesPresentWhenStrikeExpired"/> </histogram_suffixes> +<histogram_suffixes name="AutofillSuggestionAcceptedIndexSuggestionType" + separator="."> + <suffix name="CreditCard" label="Suggestions with credit card data"/> + <suffix name="Other" label="Unspecified suggestions"/> + <suffix name="Profile" + label="Suggestions with personal data from AutofillProfiles, e.g. name, + address, email address, and or phone number"/> + <affected-histogram name="Autofill.SuggestionAcceptedIndex"/> +</histogram_suffixes> + <histogram_suffixes name="AutofillSyncState" separator="."> <suffix name="SignedIn" label="Signed in"/> <suffix name="SignedInAndSyncFeature" label="Signed in and sync feature">
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py index 6cf6c47..9d45779 100755 --- a/tools/perf/core/perf_data_generator.py +++ b/tools/perf/core/perf_data_generator.py
@@ -1091,7 +1091,11 @@ # to ~2 hrs. 'hard_timeout': 10 * 60 * 60, # 10 hours timeout for full suite 'ignore_task_failure': False, - 'io_timeout': 30 * 60, # 30 minutes + # 4 hour timeout. Note that this is effectively the timeout for a + # benchmarking subprocess to run since we intentionally do not stream + # subprocess output to the task stdout. + # TODO(crbug.com/865538): Reduce this once we can reduce hard_timeout. + 'io_timeout': 4 * 60 * 60, 'dimension_sets': [ tester_config['dimension'] ],
diff --git a/tools/perf/core/perf_data_generator_unittest.py b/tools/perf/core/perf_data_generator_unittest.py index 38f9a0c9..4bc1b21 100644 --- a/tools/perf/core/perf_data_generator_unittest.py +++ b/tools/perf/core/perf_data_generator_unittest.py
@@ -53,206 +53,6 @@ os.remove(fake_perf_waterfall_file) - def testGenerateCPlusPlusTestSuite(self): - swarming_dimensions = [ - {'os': 'SkyNet', 'pool': 'T-RIP'} - ] - test_config = { - 'platform': 'win', - 'dimension': swarming_dimensions, - } - test = { - 'isolate': 'angle_perftest', - 'num_shards': 1, - 'type': perf_data_generator.TEST_TYPES.GTEST, - } - returned_test = perf_data_generator.generate_performance_test( - test_config, test) - - expected_generated_test = { - 'override_compile_targets': ['angle_perftest'], - 'isolate_name': 'angle_perftest', - 'args': ['--gtest-benchmark-name', 'angle_perftest'], - 'trigger_script': { - 'args': [ - '--multiple-dimension-script-verbose', - 'True' - ], - 'requires_simultaneous_shard_dispatch': True, - 'script': '//testing/trigger_scripts/perf_device_trigger.py' - }, - 'merge': { - 'script': '//tools/perf/process_perf_results.py' - }, - 'swarming': { - 'ignore_task_failure': False, - 'can_use_on_swarming_builders': True, - 'expiration': 7200, - 'io_timeout': 1800, - 'hard_timeout': 36000, - 'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]], - 'shards': 1 - }, - 'name': 'angle_perftest' - } - self.assertEquals(returned_test, expected_generated_test) - - def testGeneratePerformanceTestSuiteExact(self): - swarming_dimensions = [ - {'os': 'SkyNet', 'pool': 'T-RIP'} - ] - test_config = { - 'platform': 'android-webview', - 'browser': 'bin/monochrome_64_32_bundle', - 'dimension': swarming_dimensions, - } - test = { - 'isolate': 'performance_test_suite', - 'extra_args': [ - '--run-ref-build', - '--test-shard-map-filename=shard_map.json', - ], - 'num_shards': 26 - } - returned_test = perf_data_generator.generate_performance_test( - test_config, test) - - expected_generated_test = { - 'override_compile_targets': ['performance_test_suite'], - 'isolate_name': 'performance_test_suite', - 'args': ['-v', '--browser=exact', '--upload-results', - '--browser-executable=../../out/Release' - '/bin/monochrome_64_32_bundle', - '--device=android', - '--webview-embedder-apk=../../out/Release' - '/apks/SystemWebViewShell.apk', - '--run-ref-build', - '--test-shard-map-filename=shard_map.json'], - 'trigger_script': { - 'args': [ - '--multiple-dimension-script-verbose', - 'True' - ], - 'requires_simultaneous_shard_dispatch': True, - 'script': '//testing/trigger_scripts/perf_device_trigger.py' - }, - 'merge': { - 'script': '//tools/perf/process_perf_results.py' - }, - 'swarming': { - 'ignore_task_failure': False, - 'can_use_on_swarming_builders': True, - 'expiration': 7200, - 'io_timeout': 1800, - 'hard_timeout': 36000, - 'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]], - 'shards': 26 - }, - 'name': 'performance_test_suite' - } - self.assertEquals(returned_test, expected_generated_test) - - def testGeneratePerformanceTestSuiteWebview(self): - swarming_dimensions = [ - {'os': 'SkyNet', 'pool': 'T-RIP'} - ] - test_config = { - 'platform': 'android-webview', - 'dimension': swarming_dimensions, - } - test = { - 'isolate': 'performance_test_suite', - 'extra_args': [ - '--run-ref-build', - '--test-shard-map-filename=shard_map.json', - ], - 'num_shards': 26 - } - returned_test = perf_data_generator.generate_performance_test( - test_config, test) - - expected_generated_test = { - 'override_compile_targets': ['performance_test_suite'], - 'isolate_name': 'performance_test_suite', - 'args': ['-v', '--browser=android-webview', '--upload-results', - '--webview-embedder-apk=../../out/Release' - '/apks/SystemWebViewShell.apk', - '--run-ref-build', - '--test-shard-map-filename=shard_map.json'], - 'trigger_script': { - 'args': [ - '--multiple-dimension-script-verbose', - 'True' - ], - 'requires_simultaneous_shard_dispatch': True, - 'script': '//testing/trigger_scripts/perf_device_trigger.py' - }, - 'merge': { - 'script': '//tools/perf/process_perf_results.py' - }, - 'swarming': { - 'ignore_task_failure': False, - 'can_use_on_swarming_builders': True, - 'expiration': 7200, - 'io_timeout': 1800, - 'hard_timeout': 36000, - 'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]], - 'shards': 26 - }, - 'name': 'performance_test_suite' - } - self.assertEquals(returned_test, expected_generated_test) - - def testGeneratePerformanceTestSuite(self): - swarming_dimensions = [ - {'os': 'SkyNet', 'pool': 'T-RIP'} - ] - test_config = { - 'platform': 'android', - 'dimension': swarming_dimensions, - } - test = { - 'isolate': 'performance_test_suite', - 'extra_args': [ - '--run-ref-build', - '--test-shard-map-filename=shard_map.json', - ], - 'num_shards': 26 - } - returned_test = perf_data_generator.generate_performance_test( - test_config, test) - - expected_generated_test = { - 'override_compile_targets': ['performance_test_suite'], - 'isolate_name': 'performance_test_suite', - 'args': ['-v', '--browser=android-chromium', '--upload-results', - '--run-ref-build', - '--test-shard-map-filename=shard_map.json'], - 'trigger_script': { - 'args': [ - '--multiple-dimension-script-verbose', - 'True' - ], - 'requires_simultaneous_shard_dispatch': True, - 'script': '//testing/trigger_scripts/perf_device_trigger.py' - }, - 'merge': { - 'script': '//tools/perf/process_perf_results.py' - }, - 'swarming': { - 'ignore_task_failure': False, - 'can_use_on_swarming_builders': True, - 'expiration': 7200, - 'io_timeout': 1800, - 'hard_timeout': 36000, - 'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]], - 'shards': 26 - }, - 'name': 'performance_test_suite' - } - self.assertEquals(returned_test, expected_generated_test) - - class TestIsPerfBenchmarksSchedulingValid(unittest.TestCase): def setUp(self): self.maxDiff = None
diff --git a/ui/base/cursor/OWNERS b/ui/base/cursor/OWNERS deleted file mode 100644 index c3e460e..0000000 --- a/ui/base/cursor/OWNERS +++ /dev/null
@@ -1 +0,0 @@ -per-file *x11*=derat@chromium.org
diff --git a/ui/base/x/OWNERS b/ui/base/x/OWNERS index 20cb707..9b39f6f 100644 --- a/ui/base/x/OWNERS +++ b/ui/base/x/OWNERS
@@ -1,3 +1,3 @@ -davemoore@chromium.org +piman@chromium.org sadrul@chromium.org -derat@chromium.org +thomasanderson@chromium.org
diff --git a/ui/chromeos/OWNERS b/ui/chromeos/OWNERS index 94f5eddc..734b95a3 100644 --- a/ui/chromeos/OWNERS +++ b/ui/chromeos/OWNERS
@@ -1,4 +1,3 @@ -derat@chromium.org jamescook@chromium.org oshima@chromium.org stevenjb@chromium.org
diff --git a/ui/display/OWNERS b/ui/display/OWNERS index 0101ef32..6f13619 100644 --- a/ui/display/OWNERS +++ b/ui/display/OWNERS
@@ -1,5 +1,4 @@ afakhry@chromium.org -derat@chromium.org dnicoara@chromium.org marcheu@chromium.org oshima@chromium.org
diff --git a/ui/display/mac/display_link_mac.cc b/ui/display/mac/display_link_mac.cc index 29d6b303..6e0f7cf 100644 --- a/ui/display/mac/display_link_mac.cc +++ b/ui/display/mac/display_link_mac.cc
@@ -70,9 +70,6 @@ if (!display_id) return nullptr; - // Ensure the main thread is captured. - GetMainThreadTaskRunner(); - // Return the existing display link for this display, if it exists. DisplayLinkMap& all_display_links = GetAllDisplayLinks(); auto found = all_display_links.find(display_id); @@ -102,6 +99,36 @@ return display_link_mac; } +bool DisplayLinkMac::GetVSyncParameters(base::TimeTicks* timebase, + base::TimeDelta* interval) { + if (!timebase_and_interval_valid_) { + StartOrContinueDisplayLink(); + return false; + } + + // The vsync parameters skew over time (astonishingly quickly -- 0.1 msec per + // second). If too much time has elapsed since the last time the vsync + // parameters were calculated, re-calculate them (but still return the old + // parameters -- the update will be asynchronous). + if (base::TimeTicks::Now() >= recalculate_time_) + StartOrContinueDisplayLink(); + + *timebase = timebase_; + *interval = interval_; + return true; +} + +double DisplayLinkMac::GetRefreshRate() { + double refresh_rate = 0; + CVTime cv_time = + CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link_); + if (!(cv_time.flags & kCVTimeIsIndefinite)) + refresh_rate = (static_cast<double>(cv_time.timeScale) / + static_cast<double>(cv_time.timeValue)); + + return refresh_rate; +} + DisplayLinkMac::DisplayLinkMac( CGDirectDisplayID display_id, base::ScopedTypeRef<CVDisplayLinkRef> display_link) @@ -133,25 +160,6 @@ } } -bool DisplayLinkMac::GetVSyncParameters(base::TimeTicks* timebase, - base::TimeDelta* interval) { - if (!timebase_and_interval_valid_) { - StartOrContinueDisplayLink(); - return false; - } - - // The vsync parameters skew over time (astonishingly quickly -- 0.1 msec per - // second). If too much time has elapsed since the last time the vsync - // parameters were calculated, re-calculate them (but still return the old - // parameters -- the update will be asynchronous). - if (base::TimeTicks::Now() >= recalculate_time_) - StartOrContinueDisplayLink(); - - *timebase = timebase_; - *interval = interval_; - return true; -} - // static void DisplayLinkMac::DoUpdateVSyncParameters(CGDirectDisplayID display, const CVTimeStamp& time) { @@ -207,6 +215,10 @@ if (CVDisplayLinkIsRunning(display_link_)) return; + // Ensure the main thread is captured. + if (!task_runner_) + task_runner_ = GetMainThreadTaskRunner(); + CVReturn ret = CVDisplayLinkStart(display_link_); if (ret != kCVReturnSuccess) LOG(ERROR) << "CVDisplayLinkStart failed: " << ret;
diff --git a/ui/display/mac/display_link_mac.h b/ui/display/mac/display_link_mac.h index c7e23ba..3db8d3cb 100644 --- a/ui/display/mac/display_link_mac.h +++ b/ui/display/mac/display_link_mac.h
@@ -11,6 +11,7 @@ #include "base/mac/scoped_typeref.h" #include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" #include "base/time/time.h" #include "ui/display/display_export.h" @@ -27,6 +28,9 @@ // are invalid. bool GetVSyncParameters(base::TimeTicks* timebase, base::TimeDelta* interval); + // Get the panel/monitor refresh rate + double GetRefreshRate(); + private: friend class base::RefCountedThreadSafe<DisplayLinkMac>; @@ -66,6 +70,9 @@ // CVDisplayLink for querying VSync timing info. base::ScopedTypeRef<CVDisplayLinkRef> display_link_; + // The task runner to post tasks to from the display link thread. + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + // VSync parameters computed during UpdateVSyncParameters(). bool timebase_and_interval_valid_ = false; base::TimeTicks timebase_;
diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm index 4d5b83a..170963e 100644 --- a/ui/display/mac/screen_mac.mm +++ b/ui/display/mac/screen_mac.mm
@@ -21,6 +21,7 @@ #include "base/timer/timer.h" #include "ui/display/display.h" #include "ui/display/display_change_notifier.h" +#include "ui/display/mac/display_link_mac.h" #include "ui/gfx/icc_profile.h" #include "ui/gfx/mac/coordinate_conversion.h" @@ -109,6 +110,9 @@ display.set_depth_per_component(NSBitsPerSampleFromDepth([screen depth])); display.set_is_monochrome(CGDisplayUsesForceToGray()); + if (auto display_link = ui::DisplayLinkMac::GetForDisplay(display_id)) + display.set_display_frequency(display_link->GetRefreshRate()); + // CGDisplayRotation returns a double. Display::SetRotationAsDegree will // handle the unexpected situations were the angle is not a multiple of 90. display.SetRotationAsDegree(static_cast<int>(CGDisplayRotation(display_id)));
diff --git a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js index 6cf94ae..f752dd29 100644 --- a/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js +++ b/ui/file_manager/file_manager/foreground/js/list_thumbnail_loader_unittest.js
@@ -97,15 +97,17 @@ isScanningForTest = false; - directoryModel = /** @type {!DirectoryModel} */ ({ - __proto__: cr.EventTarget.prototype, - getFileList: function() { + class TestDirectoryModel extends cr.EventTarget { + getFileList() { return fileListModel; - }, - isScanning: function() { + } + isScanning() { return isScanningForTest; - }, - }); + } + } + + /** @suppress {checkTypes} */ + directoryModel = /** @type {!DirectoryModel} */ (new TestDirectoryModel()); const fakeVolumeManager = /** @type {!VolumeManager} */ ({ getVolumeInfo: function(entry) {
diff --git a/ui/gfx/OWNERS b/ui/gfx/OWNERS index 077be260c..f764a45 100644 --- a/ui/gfx/OWNERS +++ b/ui/gfx/OWNERS
@@ -12,9 +12,6 @@ per-file display*=oshima@chromium.org per-file screen*=oshima@chromium.org -# Font-rendering configuration. -per-file font_render_params*=derat@chromium.org - # Canvas painting. per-file canvas*=danakj@chromium.org
diff --git a/ui/gfx/test/OWNERS b/ui/gfx/test/OWNERS deleted file mode 100644 index fe470e6..0000000 --- a/ui/gfx/test/OWNERS +++ /dev/null
@@ -1,2 +0,0 @@ -# Testing support for Linux implementation of FontRenderParams. -per-file fontconfig_util_linux.*=derat@chromium.org
diff --git a/ui/gfx/x/OWNERS b/ui/gfx/x/OWNERS index 1b19a9d3..079174d 100644 --- a/ui/gfx/x/OWNERS +++ b/ui/gfx/x/OWNERS
@@ -1,4 +1,4 @@ -derat@chromium.org +thomasanderson@chromium.org # Adding new atoms is allowed without OWNERS review. per-file x11_atom_cache.cc=*
diff --git a/ui/gl/features.gni b/ui/gl/features.gni index a90d220..456990c 100644 --- a/ui/gl/features.gni +++ b/ui/gl/features.gni
@@ -14,5 +14,5 @@ # Should Dawn support be compiled to back the WebGPU implementation. # Also controls linking Dawn depedencies in such as SPIRV-Tools and # SPIRV-Cross - use_dawn = is_mac + use_dawn = false }
diff --git a/ui/native_theme/caption_style_mac.mm b/ui/native_theme/caption_style_mac.mm index ddc1164..75af604 100644 --- a/ui/native_theme/caption_style_mac.mm +++ b/ui/native_theme/caption_style_mac.mm
@@ -47,12 +47,14 @@ return color_utils::SkColorToRgbaString(rgba_color); } -std::string GetMABackgroundColorAsCSSColor() { +std::string GetMABackgroundColorAndOpacityAsCSSColor() { base::ScopedCFTypeRef<CGColorRef> cg_color( MACaptionAppearanceCopyBackgroundColor(kUserDomain, nullptr)); + float opacity = MACaptionAppearanceGetBackgroundOpacity(kUserDomain, nullptr); - return color_utils::SkColorToRgbaString( - skia::CGColorRefToSkColor(cg_color.get())); + SkColor rgba_color = + SkColorSetA(skia::CGColorRefToSkColor(cg_color.get()), 0xff * opacity); + return color_utils::SkColorToRgbaString(rgba_color); } // The MA text scale is a float between 0.0 and 2.0; this function converts it @@ -125,7 +127,7 @@ CaptionStyle style; style.text_color = GetMAForegroundColorAndOpacityAsCSSColor(); - style.background_color = GetMABackgroundColorAsCSSColor(); + style.background_color = GetMABackgroundColorAndOpacityAsCSSColor(); style.text_size = GetMATextScaleAsCSSPercent(); style.text_shadow = GetMATextEdgeStyleAsCSSShadow();
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc index b71efc47..44e4aca4 100644 --- a/ui/native_theme/native_theme.cc +++ b/ui/native_theme/native_theme.cc
@@ -22,14 +22,6 @@ memcpy(this, &other, sizeof(*this)); } -void NativeTheme::SetScrollbarColors(unsigned inactive_color, - unsigned active_color, - unsigned track_color) { - thumb_inactive_color_ = inactive_color; - thumb_active_color_ = active_color; - track_color_ = track_color; -} - void NativeTheme::AddObserver(NativeThemeObserver* observer) { native_theme_observers_.AddObserver(observer); } @@ -44,10 +36,7 @@ } NativeTheme::NativeTheme() - : thumb_inactive_color_(0xeaeaea), - thumb_active_color_(0xf4f4f4), - track_color_(0xd3d3d3), - is_dark_mode_(IsForcedDarkMode()), + : is_dark_mode_(IsForcedDarkMode()), is_high_contrast_(IsForcedHighContrast()) {} NativeTheme::~NativeTheme() {
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h index 15f5391..b8a8872 100644 --- a/ui/native_theme/native_theme.h +++ b/ui/native_theme/native_theme.h
@@ -284,11 +284,6 @@ // when the part is resized. virtual gfx::Rect GetNinePatchAperture(Part part) const = 0; - // Supports theme specific colors. - void SetScrollbarColors(unsigned inactive_color, - unsigned active_color, - unsigned track_color); - // Colors for GetSystemColor(). enum ColorId { // Windows @@ -441,10 +436,6 @@ is_high_contrast_ = is_high_contrast; } - unsigned int thumb_inactive_color_; - unsigned int thumb_active_color_; - unsigned int track_color_; - private: // DarkModeObserver callback. void OnParentDarkModeChanged(bool is_dark_mode);
diff --git a/ui/native_theme/native_theme_base.cc b/ui/native_theme/native_theme_base.cc index c2d9778b..b07dd41 100644 --- a/ui/native_theme/native_theme_base.cc +++ b/ui/native_theme/native_theme_base.cc
@@ -36,6 +36,10 @@ const int kSliderThumbWidth = 11; const int kSliderThumbHeight = 21; +constexpr SkColor kThumbActiveColor = SkColorSetRGB(0xF4, 0xF4, 0xF4); +constexpr SkColor kThumbInactiveColor = SkColorSetRGB(0xEA, 0xEA, 0xEA); +constexpr SkColor kTrackColor = SkColorSetRGB(0xD3, 0xD3, 0xD3); + const SkColor kSliderTrackBackgroundColor = SkColorSetRGB(0xe3, 0xdd, 0xd8); const SkColor kSliderThumbLightGrey = SkColorSetRGB(0xf4, 0xf2, 0xef); @@ -280,7 +284,7 @@ // Calculate button color. SkScalar trackHSV[3]; - SkColorToHSV(track_color_, trackHSV); + SkColorToHSV(kTrackColor, trackHSV); SkColor buttonColor = SaturateAndBrighten(trackHSV, 0, 0.2f); SkColor backgroundColor = buttonColor; if (state == kPressed) { @@ -347,7 +351,7 @@ flags.setAntiAlias(true); flags.setStyle(cc::PaintFlags::kStroke_Style); SkScalar thumbHSV[3]; - SkColorToHSV(thumb_inactive_color_, thumbHSV); + SkColorToHSV(kThumbInactiveColor, thumbHSV); flags.setColor(OutlineColor(trackHSV, thumbHSV)); canvas->drawPath(outline, flags); @@ -422,12 +426,12 @@ skrect.set(rect.x(), rect.y(), rect.right(), rect.bottom()); SkScalar track_hsv[3]; - SkColorToHSV(track_color_, track_hsv); + SkColorToHSV(kTrackColor, track_hsv); flags.setColor(SaturateAndBrighten(track_hsv, 0, 0)); canvas->drawIRect(skrect, flags); SkScalar thumb_hsv[3]; - SkColorToHSV(thumb_inactive_color_, thumb_hsv); + SkColorToHSV(kThumbInactiveColor, thumb_hsv); flags.setColor(OutlineColor(track_hsv, thumb_hsv)); DrawBox(canvas, rect, flags); @@ -444,7 +448,7 @@ const bool vertical = part == kScrollbarVerticalThumb; SkScalar thumb[3]; - SkColorToHSV(hovered ? thumb_active_color_ : thumb_inactive_color_, thumb); + SkColorToHSV(hovered ? kThumbActiveColor : kThumbInactiveColor, thumb); cc::PaintFlags flags; flags.setColor(SaturateAndBrighten(thumb, 0, 0.02f)); @@ -470,7 +474,7 @@ canvas->drawIRect(skrect, flags); SkScalar track[3]; - SkColorToHSV(track_color_, track); + SkColorToHSV(kTrackColor, track); flags.setColor(OutlineColor(track, thumb)); DrawBox(canvas, rect, flags); @@ -941,9 +945,9 @@ return SK_ColorBLACK; SkScalar track_hsv[3]; - SkColorToHSV(track_color_, track_hsv); + SkColorToHSV(kTrackColor, track_hsv); SkScalar thumb_hsv[3]; - SkColorToHSV(thumb_inactive_color_, thumb_hsv); + SkColorToHSV(kThumbInactiveColor, thumb_hsv); return OutlineColor(track_hsv, thumb_hsv); }
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm b/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm index bf20c20..5a1d1f2 100644 --- a/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm +++ b/ui/views/accessibility/view_ax_platform_node_delegate_mac.mm
@@ -31,12 +31,12 @@ if (!top_level_widget) return nil; - auto* bridge_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow( + auto* window_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow( top_level_widget->GetNativeWindow()); - if (!bridge_host) + if (!window_host) return nil; - return bridge_host->GetNativeViewAccessibleForNSWindow(); + return window_host->GetNativeViewAccessibleForNSWindow(); } gfx::NativeViewAccessible ViewAXPlatformNodeDelegateMac::GetParent() { @@ -47,12 +47,12 @@ if (!widget) return nil; - auto* bridge_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow( + auto* window_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow( view()->GetWidget()->GetNativeWindow()); - if (!bridge_host) + if (!window_host) return nil; - return bridge_host->GetNativeViewAccessibleForNSView(); + return window_host->GetNativeViewAccessibleForNSView(); } } // namespace views
diff --git a/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm b/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm index c56c2f6..736f611 100644 --- a/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm +++ b/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm
@@ -257,7 +257,7 @@ HitTestNativeWidgetMac(internal::NativeWidgetDelegate* delegate, NativeFrameView* native_frame_view) : NativeWidgetMac(delegate), native_frame_view_(native_frame_view) { - bridge_host_ = std::make_unique<NativeWidgetMacNSWindowHost>(this); + ns_window_host_ = std::make_unique<NativeWidgetMacNSWindowHost>(this); } // internal::NativeWidgetPrivate:
diff --git a/ui/views/cocoa/bridged_native_widget_unittest.mm b/ui/views/cocoa/bridged_native_widget_unittest.mm index 5241c96..d925401 100644 --- a/ui/views/cocoa/bridged_native_widget_unittest.mm +++ b/ui/views/cocoa/bridged_native_widget_unittest.mm
@@ -300,8 +300,8 @@ public: explicit MockNativeWidgetMac(internal::NativeWidgetDelegate* delegate) : NativeWidgetMac(delegate) {} - using NativeWidgetMac::bridge_impl; - using NativeWidgetMac::bridge_host; + using NativeWidgetMac::GetInProcessNSWindowBridge; + using NativeWidgetMac::GetNSWindowHost; // internal::NativeWidgetPrivate: void InitNativeWidget(const Widget::InitParams& params) override { @@ -313,19 +313,19 @@ styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]); - bridge_host()->CreateLocalBridge(window); + GetNSWindowHost()->CreateInProcessNSWindowBridge(window); if (auto* parent = NativeWidgetMacNSWindowHost::GetFromNativeView(params.parent)) { - bridge_host()->SetParent(parent); + GetNSWindowHost()->SetParent(parent); } - bridge_host()->InitWindow(params); + GetNSWindowHost()->InitWindow(params); // Usually the bridge gets initialized here. It is skipped to run extra // checks in tests, and so that a second window isn't created. delegate()->OnNativeWidgetCreated(); // To allow events to dispatch to a view, it needs a way to get focus. - bridge_host()->SetFocusManager(GetWidget()->GetFocusManager()); + GetNSWindowHost()->SetFocusManager(GetWidget()->GetFocusManager()); } void ReorderNativeViews() override { @@ -350,10 +350,10 @@ : native_widget_mac_(nullptr) {} remote_cocoa::NativeWidgetNSWindowBridge* bridge() { - return native_widget_mac_->bridge_impl(); + return native_widget_mac_->GetInProcessNSWindowBridge(); } - NativeWidgetMacNSWindowHost* bridge_host() { - return native_widget_mac_->bridge_host(); + NativeWidgetMacNSWindowHost* GetNSWindowHost() { + return native_widget_mac_->GetNSWindowHost(); } // Generate an autoreleased KeyDown NSEvent* in |widget_| for pressing the @@ -405,8 +405,8 @@ } NSWindow* bridge_window() const { - if (native_widget_mac_->bridge_impl()) - return native_widget_mac_->bridge_impl()->ns_window(); + if (auto* bridge = native_widget_mac_->GetInProcessNSWindowBridge()) + return bridge->ns_window(); return nil; } @@ -551,7 +551,7 @@ // schedules a task to flash the cursor, so this requires |message_loop_|. textfield->RequestFocus(); - bridge_host()->text_input_host()->SetTextInputClient(textfield); + GetNSWindowHost()->text_input_host()->SetTextInputClient(textfield); // Initialize the dummy text view. Initializing this with NSZeroRect causes // weird NSTextView behavior on OSX 10.9. @@ -633,8 +633,8 @@ // The delegate should exist before setting the root view. EXPECT_TRUE([window delegate]); - bridge_host()->SetRootView(view_.get()); - bridge()->CreateContentView(bridge_host()->GetRootViewNSViewId(), + GetNSWindowHost()->SetRootView(view_.get()); + bridge()->CreateContentView(GetNSWindowHost()->GetRootViewNSViewId(), view_->bounds()); ns_view_ = bridge()->ns_view(); @@ -647,8 +647,8 @@ // Clear kill buffer so that no state persists between tests. TextfieldModel::ClearKillBuffer(); - if (bridge_host()) { - bridge_host()->SetRootView(nullptr); + if (GetNSWindowHost()) { + GetNSWindowHost()->SetRootView(nullptr); bridge()->DestroyContentView(); } view_.reset(); @@ -850,7 +850,7 @@ } TEST_F(BridgedNativeWidgetTest, GetInputMethodShouldNotReturnNull) { - EXPECT_TRUE(bridge_host()->GetInputMethod()); + EXPECT_TRUE(GetNSWindowHost()->GetInputMethod()); } // A simpler test harness for testing initialization flows. @@ -884,7 +884,7 @@ new MockNativeWidgetMac(nullptr)); native_widget_mac_ = native_widget.get(); EXPECT_FALSE(bridge()); - EXPECT_FALSE(bridge_host()->GetLocalNSWindow()); + EXPECT_FALSE(GetNSWindowHost()->GetInProcessNSWindow()); } // Tests the shadow type given in InitParams. @@ -931,7 +931,7 @@ EXPECT_FALSE([ns_view_ inputContext]); InstallTextField(test_string, ui::TEXT_INPUT_TYPE_TEXT); EXPECT_TRUE([ns_view_ inputContext]); - bridge_host()->text_input_host()->SetTextInputClient(nullptr); + GetNSWindowHost()->text_input_host()->SetTextInputClient(nullptr); EXPECT_FALSE([ns_view_ inputContext]); InstallTextField(test_string, ui::TEXT_INPUT_TYPE_NONE); EXPECT_FALSE([ns_view_ inputContext]); @@ -1340,7 +1340,7 @@ // Test that we don't crash during an action message even if the TextInputClient // is nil. Regression test for crbug.com/615745. TEST_F(BridgedNativeWidgetTest, NilTextInputClient) { - bridge_host()->text_input_host()->SetTextInputClient(nullptr); + GetNSWindowHost()->text_input_host()->SetTextInputClient(nullptr); NSMutableArray* selectors = [NSMutableArray array]; [selectors addObjectsFromArray:kMoveActions]; [selectors addObjectsFromArray:kSelectActions]; @@ -1778,7 +1778,7 @@ // Now, the |textfield| set above should have been set again. EXPECT_TRUE(g_fake_current_input_context); }, - &saw_update_windows, ns_view_, bridge_host(), textfield); + &saw_update_windows, ns_view_, GetNSWindowHost(), textfield); SetHandleKeyEventCallback(base::BindRepeating( [](int* saw_return_count, BridgedContentView* view, @@ -1792,7 +1792,7 @@ } return false; }, - &vkey_return_count, ns_view_, bridge_host())); + &vkey_return_count, ns_view_, GetNSWindowHost())); // Starting text (just insert it). [ns_view_ insertText:@"ã…‚" replacementRange:NSMakeRange(NSNotFound, 0)];
diff --git a/ui/views/cocoa/drag_drop_client_mac_unittest.mm b/ui/views/cocoa/drag_drop_client_mac_unittest.mm index eae4978..c4084bc 100644 --- a/ui/views/cocoa/drag_drop_client_mac_unittest.mm +++ b/ui/views/cocoa/drag_drop_client_mac_unittest.mm
@@ -158,7 +158,7 @@ DragDropClientMacTest() : widget_(new Widget) {} DragDropClientMac* drag_drop_client() { - return bridge_host_->drag_drop_client(); + return ns_window_host_->drag_drop_client(); } NSDragOperation DragUpdate(NSPasteboard* pasteboard) { @@ -189,9 +189,9 @@ gfx::Rect bounds(0, 0, 100, 100); widget_->SetBounds(bounds); - bridge_host_ = NativeWidgetMacNSWindowHost::GetFromNativeWindow( + ns_window_host_ = NativeWidgetMacNSWindowHost::GetFromNativeWindow( widget_->GetNativeWindow()); - bridge_ = bridge_host_->bridge_impl(); + bridge_ = ns_window_host_->GetInProcessNSWindowBridge(); widget_->Show(); target_ = new DragDropView(); @@ -210,7 +210,7 @@ protected: Widget* widget_ = nullptr; remote_cocoa::NativeWidgetNSWindowBridge* bridge_ = nullptr; - NativeWidgetMacNSWindowHost* bridge_host_ = nullptr; + NativeWidgetMacNSWindowHost* ns_window_host_ = nullptr; DragDropView* target_ = nullptr; base::scoped_nsobject<MockDraggingInfo> dragging_info_; @@ -242,7 +242,7 @@ // since the runloop will exit before the system has any opportunity to // capture anything. bridge_->AcquireCapture(); - EXPECT_TRUE(bridge_host_->IsMouseCaptureActive()); + EXPECT_TRUE(ns_window_host_->IsMouseCaptureActive()); // Create the drop data OSExchangeData data; @@ -268,7 +268,7 @@ target_, data, 0, ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); // The capture should be released. - EXPECT_FALSE(bridge_host_->IsMouseCaptureActive()); + EXPECT_FALSE(ns_window_host_->IsMouseCaptureActive()); } // Tests if the drag and drop target rejects the dropped data with the
diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.h b/ui/views/cocoa/native_widget_mac_ns_window_host.h index aab57676..cc22aeee 100644 --- a/ui/views/cocoa/native_widget_mac_ns_window_host.h +++ b/ui/views/cocoa/native_widget_mac_ns_window_host.h
@@ -96,7 +96,7 @@ // A NSWindow that is guaranteed to exist in this process. If the bridge // object for this host is in this process, then this points to the bridge's // NSWindow. Otherwise, it mirrors the id and bounds of the child window. - NativeWidgetMacNSWindow* GetLocalNSWindow() const; + NativeWidgetMacNSWindow* GetInProcessNSWindow() const; // Return the accessibility object for the content NSView. gfx::NativeViewAccessible GetNativeViewAccessibleForNSView() const; @@ -105,14 +105,15 @@ gfx::NativeViewAccessible GetNativeViewAccessibleForNSWindow() const; // The mojo interface through which to communicate with the underlying - // NSWindow and NSView. - remote_cocoa::mojom::NativeWidgetNSWindow* bridge() const; + // NSWindow and NSView. This points to either |remote_ns_window_ptr_| or + // |in_process_ns_window_bridge_|. + remote_cocoa::mojom::NativeWidgetNSWindow* GetNSWindowMojo() const; // Direct access to the NativeWidgetNSWindowBridge that this is hosting. // TODO(ccameron): Remove all accesses to this member, and replace them // with methods that may be sent across processes. - remote_cocoa::NativeWidgetNSWindowBridge* bridge_impl() const { - return bridge_impl_.get(); + remote_cocoa::NativeWidgetNSWindowBridge* GetInProcessNSWindowBridge() const { + return in_process_ns_window_bridge_.get(); } TooltipManager* tooltip_manager() { return tooltip_manager_.get(); } @@ -122,10 +123,11 @@ } // Create and set the bridge object to be in this process. - void CreateLocalBridge(base::scoped_nsobject<NativeWidgetMacNSWindow> window); + void CreateInProcessNSWindowBridge( + base::scoped_nsobject<NativeWidgetMacNSWindow> window); // Create and set the bridge object to be potentially in another process. - void CreateRemoteBridge( + void CreateRemoteNSWindow( remote_cocoa::ApplicationHost* application_host, remote_cocoa::mojom::CreateWindowParamsPtr window_create_params); @@ -225,9 +227,9 @@ void RankNSViewsRecursive(View* view, std::map<NSView*, int>* rank) const; // If we are accessing the BridgedNativeWidget through mojo, then - // |local_window_| is not the true window that is resized. This function - // updates the frame of |local_window_| to keep it in sync for any native - // calls that may use it (e.g, for context menu positioning). + // |in_process_ns_window_| is not the true window that is resized. This + // function updates the frame of |in_process_ns_window_| to keep it in sync + // for any native calls that may use it (e.g, for context menu positioning). void UpdateLocalWindowFrame(const gfx::Rect& frame); // NativeWidgetNSWindowHostHelper: @@ -385,8 +387,8 @@ NativeWidgetMacNSWindowHost* parent_ = nullptr; std::vector<NativeWidgetMacNSWindowHost*> children_; - // The factory that was used to create |bridge_ptr_|. This must be the same - // as |parent_->application_host_|. + // The factory that was used to create |remote_ns_window_ptr_|. This must be + // the same as |parent_->application_host_|. remote_cocoa::ApplicationHost* application_host_ = nullptr; Widget::InitParams::Type widget_type_ = Widget::InitParams::TYPE_WINDOW; @@ -401,7 +403,7 @@ // The mojo pointer to a BridgedNativeWidget, which may exist in another // process. - remote_cocoa::mojom::NativeWidgetNSWindowAssociatedPtr bridge_ptr_; + remote_cocoa::mojom::NativeWidgetNSWindowAssociatedPtr remote_ns_window_ptr_; // Remote accessibility objects corresponding to the NSWindow and its root // NSView. @@ -416,13 +418,16 @@ // TODO(ccameron): Rather than instantiate a NativeWidgetNSWindowBridge here, // we will instantiate a mojo NativeWidgetNSWindowBridge interface to a Cocoa // instance that may be in another process. - std::unique_ptr<remote_cocoa::NativeWidgetNSWindowBridge> bridge_impl_; + std::unique_ptr<remote_cocoa::NativeWidgetNSWindowBridge> + in_process_ns_window_bridge_; - // Window that is guaranteed to exist in this process (see GetLocalNSWindow). - base::scoped_nsobject<NativeWidgetMacNSWindow> local_window_; + // Window that is guaranteed to exist in this process (see + // GetInProcessNSWindow). + base::scoped_nsobject<NativeWidgetMacNSWindow> in_process_ns_window_; - // Id mapping for |local_window_|'s content NSView. - std::unique_ptr<remote_cocoa::ScopedNSViewIdMapping> local_view_id_mapping_; + // Id mapping for |in_process_ns_window_|'s content NSView. + std::unique_ptr<remote_cocoa::ScopedNSViewIdMapping> + in_process_view_id_mapping_; std::unique_ptr<TooltipManager> tooltip_manager_; std::unique_ptr<ui::InputMethod> input_method_; @@ -463,7 +468,7 @@ std::map<const views::View*, NSView*> associated_views_; mojo::AssociatedBinding<remote_cocoa::mojom::NativeWidgetNSWindowHost> - host_mojo_binding_; + remote_ns_window_host_binding_; DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacNSWindowHost); };
diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm index 6585b7f..b0afad2 100644 --- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm +++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm
@@ -297,7 +297,7 @@ root_view_id_(remote_cocoa::GetNewNSViewId()), accessibility_focus_overrider_(this), text_input_host_(new TextInputHost(this)), - host_mojo_binding_(this) { + remote_ns_window_host_binding_(this) { DCHECK(GetIdToWidgetHostImplMap().find(widget_id_) == GetIdToWidgetHostImplMap().end()); GetIdToWidgetHostImplMap().insert(std::make_pair(widget_id_, this)); @@ -307,14 +307,14 @@ NativeWidgetMacNSWindowHost::~NativeWidgetMacNSWindowHost() { DCHECK(children_.empty()); if (application_host_) { - bridge_ptr_.reset(); + remote_ns_window_ptr_.reset(); application_host_->RemoveObserver(this); application_host_ = nullptr; } // Workaround for https://crbug.com/915572 - if (host_mojo_binding_.is_bound()) { - auto request = host_mojo_binding_.Unbind(); + if (remote_ns_window_host_binding_.is_bound()) { + auto request = remote_ns_window_host_binding_.Unbind(); if (request.is_pending()) { mojo::MakeStrongAssociatedBinding( std::make_unique<BridgedNativeWidgetHostDummy>(), std::move(request)); @@ -331,47 +331,49 @@ // destruction. // TODO(ccameron): When all communication from |bridge_| to this goes through // the BridgedNativeWidgetHost, this can be replaced with closing that pipe. - bridge_impl_.reset(); + in_process_ns_window_bridge_.reset(); SetFocusManager(nullptr); DestroyCompositor(); } -NativeWidgetMacNSWindow* NativeWidgetMacNSWindowHost::GetLocalNSWindow() const { - return local_window_.get(); +NativeWidgetMacNSWindow* NativeWidgetMacNSWindowHost::GetInProcessNSWindow() + const { + return in_process_ns_window_.get(); } gfx::NativeViewAccessible NativeWidgetMacNSWindowHost::GetNativeViewAccessibleForNSView() const { - if (bridge_impl_) - return bridge_impl_->ns_view(); + if (in_process_ns_window_bridge_) + return in_process_ns_window_bridge_->ns_view(); return remote_view_accessible_.get(); } gfx::NativeViewAccessible NativeWidgetMacNSWindowHost::GetNativeViewAccessibleForNSWindow() const { - if (bridge_impl_) - return bridge_impl_->ns_window(); + if (in_process_ns_window_bridge_) + return in_process_ns_window_bridge_->ns_window(); return remote_window_accessible_.get(); } -remote_cocoa::mojom::NativeWidgetNSWindow* NativeWidgetMacNSWindowHost::bridge() - const { - if (bridge_ptr_) - return bridge_ptr_.get(); - if (bridge_impl_) - return bridge_impl_.get(); +remote_cocoa::mojom::NativeWidgetNSWindow* +NativeWidgetMacNSWindowHost::GetNSWindowMojo() const { + if (remote_ns_window_ptr_) + return remote_ns_window_ptr_.get(); + if (in_process_ns_window_bridge_) + return in_process_ns_window_bridge_.get(); return nullptr; } -void NativeWidgetMacNSWindowHost::CreateLocalBridge( +void NativeWidgetMacNSWindowHost::CreateInProcessNSWindowBridge( base::scoped_nsobject<NativeWidgetMacNSWindow> window) { - local_window_ = window; - bridge_impl_ = std::make_unique<remote_cocoa::NativeWidgetNSWindowBridge>( - widget_id_, this, this, text_input_host_.get()); - bridge_impl_->SetWindow(window); + in_process_ns_window_ = window; + in_process_ns_window_bridge_ = + std::make_unique<remote_cocoa::NativeWidgetNSWindowBridge>( + widget_id_, this, this, text_input_host_.get()); + in_process_ns_window_bridge_->SetWindow(window); } -void NativeWidgetMacNSWindowHost::CreateRemoteBridge( +void NativeWidgetMacNSWindowHost::CreateRemoteNSWindow( remote_cocoa::ApplicationHost* application_host, remote_cocoa::mojom::CreateWindowParamsPtr window_create_params) { accessibility_focus_overrider_.SetAppIsRemote(true); @@ -381,30 +383,33 @@ // Create a local invisible window that will be used as the gfx::NativeWindow // handle to track this window in this process. { - auto local_window_create_params = + auto in_process_ns_window_create_params = remote_cocoa::mojom::CreateWindowParams::New(); - local_window_create_params->style_mask = NSBorderlessWindowMask; - local_window_ = remote_cocoa::NativeWidgetNSWindowBridge::CreateNSWindow( - local_window_create_params.get()); - [local_window_ setBridgedNativeWidgetId:widget_id_]; - [local_window_ setAlphaValue:0.0]; - local_view_id_mapping_ = + in_process_ns_window_create_params->style_mask = NSBorderlessWindowMask; + in_process_ns_window_ = + remote_cocoa::NativeWidgetNSWindowBridge::CreateNSWindow( + in_process_ns_window_create_params.get()); + [in_process_ns_window_ setBridgedNativeWidgetId:widget_id_]; + [in_process_ns_window_ setAlphaValue:0.0]; + in_process_view_id_mapping_ = std::make_unique<remote_cocoa::ScopedNSViewIdMapping>( - root_view_id_, [local_window_ contentView]); + root_view_id_, [in_process_ns_window_ contentView]); } - // Initialize |bridge_ptr_| to point to a bridge created by |factory|. + // Initialize |remote_ns_window_ptr_| to point to a bridge created by + // |factory|. remote_cocoa::mojom::NativeWidgetNSWindowHostAssociatedPtr host_ptr; - host_mojo_binding_.Bind(mojo::MakeRequest(&host_ptr), - ui::WindowResizeHelperMac::Get()->task_runner()); + remote_ns_window_host_binding_.Bind( + mojo::MakeRequest(&host_ptr), + ui::WindowResizeHelperMac::Get()->task_runner()); remote_cocoa::mojom::TextInputHostAssociatedPtr text_input_host_ptr; text_input_host_->BindRequest(mojo::MakeRequest(&text_input_host_ptr)); application_host_->GetApplication()->CreateNativeWidgetNSWindow( - widget_id_, mojo::MakeRequest(&bridge_ptr_), host_ptr.PassInterface(), - text_input_host_ptr.PassInterface()); + widget_id_, mojo::MakeRequest(&remote_ns_window_ptr_), + host_ptr.PassInterface(), text_input_host_ptr.PassInterface()); // Create the window in its process, and attach it to its parent window. - bridge()->CreateWindow(std::move(window_create_params)); + GetNSWindowMojo()->CreateWindow(std::move(window_create_params)); } void NativeWidgetMacNSWindowHost::InitWindow(const Widget::InitParams& params) { @@ -413,7 +418,7 @@ // native on Mac, so nothing should ever want one in Widget form. DCHECK_NE(params.type, Widget::InitParams::TYPE_TOOLTIP); widget_type_ = params.type; - tooltip_manager_.reset(new TooltipManagerMac(bridge())); + tooltip_manager_.reset(new TooltipManagerMac(GetNSWindowMojo())); // Initialize the window. { @@ -449,7 +454,7 @@ widget_type_ == Widget::InitParams::TYPE_WINDOW && params.remove_standard_frame; - bridge()->InitWindow(std::move(window_params)); + GetNSWindowMojo()->InitWindow(std::move(window_params)); } // Set a meaningful initial bounds. Note that except for frameless widgets @@ -459,22 +464,22 @@ // before calling Widget::Show() to avoid a kWindowSizeDeterminedLater-sized // (i.e. 1x1) window appearing. UpdateLocalWindowFrame(params.bounds); - bridge()->SetInitialBounds(params.bounds, widget->GetMinimumSize()); + GetNSWindowMojo()->SetInitialBounds(params.bounds, widget->GetMinimumSize()); - // TODO(ccameron): Correctly set these based |local_window_|. + // TODO(ccameron): Correctly set these based |in_process_ns_window_|. window_bounds_in_screen_ = params.bounds; content_bounds_in_screen_ = params.bounds; // Widgets for UI controls (usually layered above web contents) start visible. if (widget_type_ == Widget::InitParams::TYPE_CONTROL) - bridge()->SetVisibilityState(WindowVisibilityState::kShowInactive); + GetNSWindowMojo()->SetVisibilityState(WindowVisibilityState::kShowInactive); } void NativeWidgetMacNSWindowHost::CloseWindowNow() { - bool is_out_of_process = !bridge_impl_; + bool is_out_of_process = !in_process_ns_window_bridge_; // Note that CloseWindowNow may delete |this| for in-process windows. - if (bridge()) - bridge()->CloseWindowNow(); + if (GetNSWindowMojo()) + GetNSWindowMojo()->CloseWindowNow(); // If it is out-of-process, then simulate the calls that would have been // during window closure. @@ -488,8 +493,8 @@ void NativeWidgetMacNSWindowHost::SetBounds(const gfx::Rect& bounds) { UpdateLocalWindowFrame(bounds); - bridge()->SetBounds(bounds, - native_widget_mac_->GetWidget()->GetMinimumSize()); + GetNSWindowMojo()->SetBounds( + bounds, native_widget_mac_->GetWidget()->GetMinimumSize()); } void NativeWidgetMacNSWindowHost::SetFullscreen(bool fullscreen) { @@ -499,16 +504,16 @@ // transition (and therefore OnWindowFullscreenTransitionStart will not be // called until the current transition completes). target_fullscreen_state_ = fullscreen; - bridge()->SetFullscreen(target_fullscreen_state_); + GetNSWindowMojo()->SetFullscreen(target_fullscreen_state_); } void NativeWidgetMacNSWindowHost::SetRootView(views::View* root_view) { root_view_ = root_view; if (root_view_) { // TODO(ccameron): Drag-drop functionality does not yet run over mojo. - if (bridge_impl_) { - drag_drop_client_.reset( - new DragDropClientMac(bridge_impl_.get(), root_view_)); + if (in_process_ns_window_bridge_) { + drag_drop_client_.reset(new DragDropClientMac( + in_process_ns_window_bridge_.get(), root_view_)); } } else { drag_drop_client_.reset(); @@ -550,7 +555,7 @@ if (is_visible_) compositor_->Unsuspend(); - bridge()->InitCompositorView(); + GetNSWindowMojo()->InitCompositorView(); } void NativeWidgetMacNSWindowHost::UpdateCompositorProperties() { @@ -606,7 +611,7 @@ if (window_title_ == title) return false; window_title_ = title; - bridge()->SetWindowTitle(window_title_); + GetNSWindowMojo()->SetWindowTitle(window_title_); return true; } @@ -619,12 +624,12 @@ bool NativeWidgetMacNSWindowHost::RedispatchKeyEvent(NSEvent* event) { // If the target window is in-process, then redispatch the event directly, // and give an accurate return value. - if (bridge_impl_) - return bridge_impl_->RedispatchKeyEvent(event); + if (in_process_ns_window_bridge_) + return in_process_ns_window_bridge_->RedispatchKeyEvent(event); // If the target window is out of process then always report the event as // handled (because it should never be handled in this process). - bridge()->RedispatchKeyEvent( + GetNSWindowMojo()->RedispatchKeyEvent( [event type], [event modifierFlags], [event timestamp], base::SysNSStringToUTF16([event characters]), base::SysNSStringToUTF16([event charactersIgnoringModifiers]), @@ -686,16 +691,16 @@ if (new_application_host != application_host_) { DLOG(ERROR) << "Cannot migrate views::NativeWidget to another process, " "closing it instead."; - bridge()->CloseWindow(); + GetNSWindowMojo()->CloseWindow(); return; } parent_ = new_parent; if (parent_) { parent_->children_.push_back(this); - bridge()->SetParent(parent_->bridged_native_widget_id()); + GetNSWindowMojo()->SetParent(parent_->bridged_native_widget_id()); } else { - bridge()->SetParent(0); + GetNSWindowMojo()->SetParent(0); } } @@ -718,8 +723,8 @@ return; std::map<NSView*, int> rank; RankNSViewsRecursive(widget->GetRootView(), &rank); - if (bridge_impl_) - bridge_impl_->SortSubviews(std::move(rank)); + if (in_process_ns_window_bridge_) + in_process_ns_window_bridge_->SortSubviews(std::move(rank)); } void NativeWidgetMacNSWindowHost::RankNSViewsRecursive( @@ -734,9 +739,11 @@ void NativeWidgetMacNSWindowHost::UpdateLocalWindowFrame( const gfx::Rect& frame) { - if (!bridge_ptr_) + if (!remote_ns_window_ptr_) return; - [local_window_ setFrame:gfx::ScreenRectToNSRect(frame) display:NO animate:NO]; + [in_process_ns_window_ setFrame:gfx::ScreenRectToNSRect(frame) + display:NO + animate:NO]; } // static @@ -1380,7 +1387,7 @@ void NativeWidgetMacNSWindowHost::OnDialogChanged() { // Note it's only necessary to clear the TouchBar. If the OS needs it again, // a new one will be created. - bridge()->ClearTouchBar(); + GetNSWindowMojo()->ClearTouchBar(); } //////////////////////////////////////////////////////////////////////////////// @@ -1458,7 +1465,7 @@ // mach port has been specified (in practice, when software compositing is // enabled). // https://crbug.com/942213 - if (bridge_ptr_ && ca_layer_params->io_surface_mach_port) { + if (remote_ns_window_ptr_ && ca_layer_params->io_surface_mach_port) { gfx::CALayerParams updated_ca_layer_params = *ca_layer_params; if (!io_surface_to_remote_layer_interceptor_) { io_surface_to_remote_layer_interceptor_ = @@ -1466,9 +1473,9 @@ } io_surface_to_remote_layer_interceptor_->UpdateCALayerParams( &updated_ca_layer_params); - bridge_ptr_->SetCALayerParams(updated_ca_layer_params); + remote_ns_window_ptr_->SetCALayerParams(updated_ca_layer_params); } else { - bridge()->SetCALayerParams(*ca_layer_params); + GetNSWindowMojo()->SetCALayerParams(*ca_layer_params); } }
diff --git a/ui/views/cocoa/text_input_host.mm b/ui/views/cocoa/text_input_host.mm index 22a29b7..2af42b5 100644 --- a/ui/views/cocoa/text_input_host.mm +++ b/ui/views/cocoa/text_input_host.mm
@@ -175,8 +175,8 @@ text_input_client_ = new_text_input_client; pending_text_input_client_ = new_text_input_client; - if (host_impl_->bridge_impl_ && - host_impl_->bridge_impl_->NeedsUpdateWindows()) { + if (host_impl_->in_process_ns_window_bridge_ && + host_impl_->in_process_ns_window_bridge_->NeedsUpdateWindows()) { text_input_client_ = old_text_input_client; [NSApp updateWindows]; // Note: |pending_text_input_client_| (and therefore +[NSTextInputContext
diff --git a/ui/views/controls/native/native_view_host_mac.h b/ui/views/controls/native/native_view_host_mac.h index 2bd4dbf..01b34f7 100644 --- a/ui/views/controls/native/native_view_host_mac.h +++ b/ui/views/controls/native/native_view_host_mac.h
@@ -57,7 +57,7 @@ private: // Return the NativeWidgetMacNSWindowHost for this hosted view. - NativeWidgetMacNSWindowHost* GetBridgedNativeWidgetHost() const; + NativeWidgetMacNSWindowHost* GetNSWindowHost() const; // Our associated NativeViewHost. Owns this. NativeViewHost* host_;
diff --git a/ui/views/controls/native/native_view_host_mac.mm b/ui/views/controls/native/native_view_host_mac.mm index 8182eba..be0e09c 100644 --- a/ui/views/controls/native/native_view_host_mac.mm +++ b/ui/views/controls/native/native_view_host_mac.mm
@@ -40,8 +40,7 @@ NativeViewHostMac::~NativeViewHostMac() { } -NativeWidgetMacNSWindowHost* NativeViewHostMac::GetBridgedNativeWidgetHost() - const { +NativeWidgetMacNSWindowHost* NativeViewHostMac::GetNSWindowHost() const { return NativeWidgetMacNSWindowHost::GetFromNativeWindow( host_->GetWidget()->GetNativeWindow()); } @@ -55,17 +54,17 @@ remote_cocoa::mojom::Application* NativeViewHostMac::GetRemoteCocoaApplication() const { - if (auto* bridge_host = GetBridgedNativeWidgetHost()) { - if (auto* application_host = bridge_host->application_host()) + if (auto* window_host = GetNSWindowHost()) { + if (auto* application_host = window_host->application_host()) return application_host->GetApplication(); } return nullptr; } uint64_t NativeViewHostMac::GetNSViewId() const { - auto* bridge_host = GetBridgedNativeWidgetHost(); - if (bridge_host) - return bridge_host->GetRootViewNSViewId(); + auto* window_host = GetNSWindowHost(); + if (window_host) + return window_host->GetRootViewNSViewId(); return 0; } @@ -88,13 +87,13 @@ } EnsureNativeViewHasNoChildWidgets(native_view_); - auto* bridge_host = GetBridgedNativeWidgetHost(); - CHECK(bridge_host); + auto* window_host = GetNSWindowHost(); + CHECK(window_host); // TODO(https://crbug.com/933679): This is lifted out the ViewsHostableAttach // call below because of crashes being observed in the field. NSView* superview = - bridge_host->native_widget_mac()->GetNativeView().GetNativeNSView(); + window_host->native_widget_mac()->GetNativeView().GetNativeNSView(); [superview addSubview:native_view_]; if (native_view_hostable_) { @@ -105,7 +104,7 @@ host_->parent()->GetNativeViewAccessible()); } - bridge_host->SetAssociationForView(host_, native_view_); + window_host->SetAssociationForView(host_, native_view_); } void NativeViewHostMac::NativeViewDetaching(bool destroyed) { @@ -131,10 +130,10 @@ } EnsureNativeViewHasNoChildWidgets(native_view_); - auto* bridge_host = GetBridgedNativeWidgetHost(); + auto* window_host = GetNSWindowHost(); // NativeWidgetNSWindowBridge can be null when Widget is closing. - if (bridge_host) - bridge_host->ClearAssociationForView(host_); + if (window_host) + window_host->ClearAssociationForView(host_); native_view_.reset(); }
diff --git a/ui/views/widget/native_widget_mac.h b/ui/views/widget/native_widget_mac.h index 23848a8..c03b37c 100644 --- a/ui/views/widget/native_widget_mac.h +++ b/ui/views/widget/native_widget_mac.h
@@ -208,10 +208,16 @@ virtual void OnWindowDestroying(gfx::NativeWindow window) {} internal::NativeWidgetDelegate* delegate() { return delegate_; } - remote_cocoa::mojom::NativeWidgetNSWindow* bridge() const; - remote_cocoa::NativeWidgetNSWindowBridge* bridge_impl() const; - NativeWidgetMacNSWindowHost* bridge_host() const { - return bridge_host_.get(); + + // Return the mojo interface for the NSWindow. The interface may be + // implemented in-process or out-of-process. + remote_cocoa::mojom::NativeWidgetNSWindow* GetNSWindowMojo() const; + + // Return the bridge structure only if this widget is in-process. + remote_cocoa::NativeWidgetNSWindowBridge* GetInProcessNSWindowBridge() const; + + NativeWidgetMacNSWindowHost* GetNSWindowHost() const { + return ns_window_host_.get(); } private: @@ -220,7 +226,7 @@ friend class views::test::WidgetTest; internal::NativeWidgetDelegate* delegate_; - std::unique_ptr<NativeWidgetMacNSWindowHost> bridge_host_; + std::unique_ptr<NativeWidgetMacNSWindowHost> ns_window_host_; Widget::InitParams::Ownership ownership_;
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm index dcdaed2..4a441b8 100644 --- a/ui/views/widget/native_widget_mac.mm +++ b/ui/views/widget/native_widget_mac.mm
@@ -72,7 +72,7 @@ NativeWidgetMac::NativeWidgetMac(internal::NativeWidgetDelegate* delegate) : delegate_(delegate), - bridge_host_(new NativeWidgetMacNSWindowHost(this)), + ns_window_host_(new NativeWidgetMacNSWindowHost(this)), ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) {} NativeWidgetMac::~NativeWidgetMac() { @@ -88,8 +88,8 @@ } void NativeWidgetMac::WindowDestroyed() { - DCHECK(bridge()); - bridge_host_.reset(); + DCHECK(GetNSWindowMojo()); + ns_window_host_.reset(); // |OnNativeWidgetDestroyed| may delete |this| if the object does not own // itself. bool should_delete_this = @@ -143,15 +143,15 @@ PopulateCreateWindowParams(params, create_window_params.get()); if (application_host) { - bridge_host_->CreateRemoteBridge(application_host, - std::move(create_window_params)); + ns_window_host_->CreateRemoteNSWindow(application_host, + std::move(create_window_params)); } else { base::scoped_nsobject<NativeWidgetMacNSWindow> window( [CreateNSWindow(create_window_params.get()) retain]); - bridge_host_->CreateLocalBridge(std::move(window)); + ns_window_host_->CreateInProcessNSWindowBridge(std::move(window)); } - bridge_host_->SetParent(parent_host); - bridge_host_->InitWindow(params); + ns_window_host_->SetParent(parent_host); + ns_window_host_->InitWindow(params); OnWindowInitialized(); // Only set always-on-top here if it is true since setting it may affect how @@ -162,15 +162,15 @@ delegate_->OnNativeWidgetCreated(); DCHECK(GetWidget()->GetRootView()); - bridge_host_->SetRootView(GetWidget()->GetRootView()); - bridge()->CreateContentView(bridge_host_->GetRootViewNSViewId(), - GetWidget()->GetRootView()->bounds()); + ns_window_host_->SetRootView(GetWidget()->GetRootView()); + GetNSWindowMojo()->CreateContentView(ns_window_host_->GetRootViewNSViewId(), + GetWidget()->GetRootView()->bounds()); if (auto* focus_manager = GetWidget()->GetFocusManager()) { - bridge()->MakeFirstResponder(); - bridge_host_->SetFocusManager(focus_manager); + GetNSWindowMojo()->MakeFirstResponder(); + ns_window_host_->SetFocusManager(focus_manager); } - bridge_host_->CreateCompositor(params); + ns_window_host_->CreateCompositor(params); if (g_init_native_widget_callback) g_init_native_widget_callback->Run(this); @@ -178,7 +178,7 @@ void NativeWidgetMac::OnWidgetInitDone() { OnSizeConstraintsChanged(); - bridge_host_->OnWidgetInitDone(); + ns_window_host_->OnWidgetInitDone(); } NonClientFrameView* NativeWidgetMac::CreateNonClientFrameView() { @@ -216,7 +216,7 @@ } gfx::NativeWindow NativeWidgetMac::GetNativeWindow() const { - return bridge_host_ ? bridge_host_->GetLocalNSWindow() : nil; + return ns_window_host_ ? ns_window_host_->GetInProcessNSWindow() : nil; } Widget* NativeWidgetMac::GetTopLevelWidget() { @@ -225,66 +225,66 @@ } const ui::Compositor* NativeWidgetMac::GetCompositor() const { - return bridge_host_ && bridge_host_->layer() - ? bridge_host_->layer()->GetCompositor() + return ns_window_host_ && ns_window_host_->layer() + ? ns_window_host_->layer()->GetCompositor() : nullptr; } const ui::Layer* NativeWidgetMac::GetLayer() const { - return bridge_host_ ? bridge_host_->layer() : nullptr; + return ns_window_host_ ? ns_window_host_->layer() : nullptr; } void NativeWidgetMac::ReorderNativeViews() { - if (bridge_host_) - bridge_host_->ReorderChildViews(); + if (ns_window_host_) + ns_window_host_->ReorderChildViews(); } void NativeWidgetMac::ViewRemoved(View* view) { DragDropClientMac* client = - bridge_host_ ? bridge_host_->drag_drop_client() : nullptr; + ns_window_host_ ? ns_window_host_->drag_drop_client() : nullptr; if (client) client->drop_helper()->ResetTargetViewIfEquals(view); } void NativeWidgetMac::SetNativeWindowProperty(const char* name, void* value) { - if (bridge_host_) - bridge_host_->SetNativeWindowProperty(name, value); + if (ns_window_host_) + ns_window_host_->SetNativeWindowProperty(name, value); } void* NativeWidgetMac::GetNativeWindowProperty(const char* name) const { - if (bridge_host_) - return bridge_host_->GetNativeWindowProperty(name); + if (ns_window_host_) + return ns_window_host_->GetNativeWindowProperty(name); return nullptr; } TooltipManager* NativeWidgetMac::GetTooltipManager() const { - if (bridge_host_) - return bridge_host_->tooltip_manager(); + if (ns_window_host_) + return ns_window_host_->tooltip_manager(); return nullptr; } void NativeWidgetMac::SetCapture() { - if (bridge()) - bridge()->AcquireCapture(); + if (GetNSWindowMojo()) + GetNSWindowMojo()->AcquireCapture(); } void NativeWidgetMac::ReleaseCapture() { - if (bridge()) - bridge()->ReleaseCapture(); + if (GetNSWindowMojo()) + GetNSWindowMojo()->ReleaseCapture(); } bool NativeWidgetMac::HasCapture() const { - return bridge_host_ && bridge_host_->IsMouseCaptureActive(); + return ns_window_host_ && ns_window_host_->IsMouseCaptureActive(); } ui::InputMethod* NativeWidgetMac::GetInputMethod() { - return bridge_host_ ? bridge_host_->GetInputMethod() : nullptr; + return ns_window_host_ ? ns_window_host_->GetInputMethod() : nullptr; } void NativeWidgetMac::CenterWindow(const gfx::Size& size) { - bridge()->SetSizeAndCenter(size, GetWidget()->GetMinimumSize()); + GetNSWindowMojo()->SetSizeAndCenter(size, GetWidget()->GetMinimumSize()); } void NativeWidgetMac::GetWindowPlacement( @@ -300,9 +300,9 @@ } bool NativeWidgetMac::SetWindowTitle(const base::string16& title) { - if (!bridge_host_) + if (!ns_window_host_) return false; - return bridge_host_->SetWindowTitle(title); + return ns_window_host_->SetWindowTitle(title); } void NativeWidgetMac::SetWindowIcons(const gfx::ImageSkia& window_icon, @@ -322,21 +322,23 @@ // A peculiarity of the constrained window framework is that it permits a // dialog of MODAL_TYPE_WINDOW to have a null parent window; falling back to // a non-modal window in this case. - DCHECK(bridge_host_->parent() || modal_type == ui::MODAL_TYPE_WINDOW); + DCHECK(ns_window_host_->parent() || modal_type == ui::MODAL_TYPE_WINDOW); // Everything happens upon show. } gfx::Rect NativeWidgetMac::GetWindowBoundsInScreen() const { - return bridge_host_ ? bridge_host_->GetWindowBoundsInScreen() : gfx::Rect(); + return ns_window_host_ ? ns_window_host_->GetWindowBoundsInScreen() + : gfx::Rect(); } gfx::Rect NativeWidgetMac::GetClientAreaBoundsInScreen() const { - return bridge_host_ ? bridge_host_->GetContentBoundsInScreen() : gfx::Rect(); + return ns_window_host_ ? ns_window_host_->GetContentBoundsInScreen() + : gfx::Rect(); } gfx::Rect NativeWidgetMac::GetRestoredBounds() const { - return bridge_host_ ? bridge_host_->GetRestoredBounds() : gfx::Rect(); + return ns_window_host_ ? ns_window_host_->GetRestoredBounds() : gfx::Rect(); } std::string NativeWidgetMac::GetWorkspace() const { @@ -344,17 +346,17 @@ } void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) { - if (bridge_host_) - bridge_host_->SetBounds(bounds); + if (ns_window_host_) + ns_window_host_->SetBounds(bounds); } void NativeWidgetMac::SetBoundsConstrained(const gfx::Rect& bounds) { - if (!bridge_host_) + if (!ns_window_host_) return; gfx::Rect new_bounds(bounds); - if (bridge_host_->parent()) { + if (ns_window_host_->parent()) { new_bounds.AdjustToFit( - gfx::Rect(bridge_host_->parent()->GetWindowBoundsInScreen().size())); + gfx::Rect(ns_window_host_->parent()->GetWindowBoundsInScreen().size())); } else { new_bounds = ConstrainBoundsToDisplayWorkArea(new_bounds); } @@ -368,7 +370,7 @@ } void NativeWidgetMac::StackAbove(gfx::NativeView native_view) { - if (!bridge()) + if (!GetNSWindowMojo()) return; auto* sibling_host = @@ -376,17 +378,17 @@ if (!sibling_host) { // This will only work if |this| is in-process. - DCHECK(!bridge_host_->application_host()); + DCHECK(!ns_window_host_->application_host()); NSInteger view_parent = native_view.GetNativeNSView().window.windowNumber; [GetNativeWindow().GetNativeNSWindow() orderWindow:NSWindowAbove relativeTo:view_parent]; return; } - if (bridge_host_->application_host() == sibling_host->application_host()) { + if (ns_window_host_->application_host() == sibling_host->application_host()) { // Check if |native_view|'s NativeWidgetMacNSWindowHost corresponds to the // same process as |this|. - bridge()->StackAbove(sibling_host->bridged_native_widget_id()); + GetNSWindowMojo()->StackAbove(sibling_host->bridged_native_widget_id()); return; } @@ -395,8 +397,8 @@ } void NativeWidgetMac::StackAtTop() { - if (bridge()) - bridge()->StackAtTop(); + if (GetNSWindowMojo()) + GetNSWindowMojo()->StackAtTop(); } void NativeWidgetMac::SetShape(std::unique_ptr<Widget::ShapeRects> shape) { @@ -404,20 +406,20 @@ } void NativeWidgetMac::Close() { - if (bridge()) - bridge()->CloseWindow(); + if (GetNSWindowMojo()) + GetNSWindowMojo()->CloseWindow(); } void NativeWidgetMac::CloseNow() { - if (bridge_host_) - bridge_host_->CloseWindowNow(); - // Note: |bridge_host_| will be deleted here, and |this| will be deleted here - // when ownership_ == NATIVE_WIDGET_OWNS_WIDGET, + if (ns_window_host_) + ns_window_host_->CloseWindowNow(); + // Note: |ns_window_host_| will be deleted here, and |this| will be deleted + // here when ownership_ == NATIVE_WIDGET_OWNS_WIDGET, } void NativeWidgetMac::Show(ui::WindowShowState show_state, const gfx::Rect& restore_bounds) { - if (!bridge()) + if (!GetNSWindowMojo()) return; switch (show_state) { @@ -439,7 +441,7 @@ window_state = WindowVisibilityState::kShowInactive; else if (show_state == ui::SHOW_STATE_MINIMIZED) window_state = WindowVisibilityState::kHideWindow; - bridge()->SetVisibilityState(window_state); + GetNSWindowMojo()->SetVisibilityState(window_state); // Ignore the SetInitialFocus() result. BridgedContentView should get // firstResponder status regardless. @@ -447,19 +449,20 @@ } void NativeWidgetMac::Hide() { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetVisibilityState(WindowVisibilityState::kHideWindow); + GetNSWindowMojo()->SetVisibilityState(WindowVisibilityState::kHideWindow); } bool NativeWidgetMac::IsVisible() const { - return bridge_host_ && bridge_host_->IsVisible(); + return ns_window_host_ && ns_window_host_->IsVisible(); } void NativeWidgetMac::Activate() { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetVisibilityState(WindowVisibilityState::kShowAndActivateWindow); + GetNSWindowMojo()->SetVisibilityState( + WindowVisibilityState::kShowAndActivateWindow); } void NativeWidgetMac::Deactivate() { @@ -467,7 +470,7 @@ } bool NativeWidgetMac::IsActive() const { - return bridge_host_ ? bridge_host_->IsWindowKey() : false; + return ns_window_host_ ? ns_window_host_->IsWindowKey() : false; } void NativeWidgetMac::SetAlwaysOnTop(bool always_on_top) { @@ -480,9 +483,9 @@ } void NativeWidgetMac::SetVisibleOnAllWorkspaces(bool always_visible) { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetVisibleOnAllSpaces(always_visible); + GetNSWindowMojo()->SetVisibleOnAllSpaces(always_visible); } bool NativeWidgetMac::IsVisibleOnAllWorkspaces() const { @@ -494,9 +497,9 @@ } void NativeWidgetMac::Minimize() { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetMiniaturized(true); + GetNSWindowMojo()->SetMiniaturized(true); } bool NativeWidgetMac::IsMaximized() const { @@ -506,46 +509,46 @@ } bool NativeWidgetMac::IsMinimized() const { - if (!bridge_host_) + if (!ns_window_host_) return false; - return bridge_host_->IsMiniaturized(); + return ns_window_host_->IsMiniaturized(); } void NativeWidgetMac::Restore() { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetFullscreen(false); - bridge()->SetMiniaturized(false); + GetNSWindowMojo()->SetFullscreen(false); + GetNSWindowMojo()->SetMiniaturized(false); } void NativeWidgetMac::SetFullscreen(bool fullscreen) { - if (!bridge_host_) + if (!ns_window_host_) return; - bridge_host_->SetFullscreen(fullscreen); + ns_window_host_->SetFullscreen(fullscreen); } bool NativeWidgetMac::IsFullscreen() const { - return bridge_host_ && bridge_host_->target_fullscreen_state(); + return ns_window_host_ && ns_window_host_->target_fullscreen_state(); } void NativeWidgetMac::SetCanAppearInExistingFullscreenSpaces( bool can_appear_in_existing_fullscreen_spaces) { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetCanAppearInExistingFullscreenSpaces( + GetNSWindowMojo()->SetCanAppearInExistingFullscreenSpaces( can_appear_in_existing_fullscreen_spaces); } void NativeWidgetMac::SetOpacity(float opacity) { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetOpacity(opacity); + GetNSWindowMojo()->SetOpacity(opacity); } void NativeWidgetMac::SetAspectRatio(const gfx::SizeF& aspect_ratio) { - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->SetContentAspectRatio(aspect_ratio); + GetNSWindowMojo()->SetContentAspectRatio(aspect_ratio); } void NativeWidgetMac::FlashFrame(bool flash_frame) { @@ -557,8 +560,8 @@ const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source) { - bridge_host_->drag_drop_client()->StartDragAndDrop(view, data, operation, - source); + ns_window_host_->drag_drop_client()->StartDragAndDrop(view, data, operation, + source); } void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) { @@ -571,8 +574,8 @@ target_rect.origin.y = NSHeight(client_rect) - target_rect.origin.y - NSHeight(target_rect); [GetNativeView().GetNativeNSView() setNeedsDisplayInRect:target_rect]; - if (bridge_host_ && bridge_host_->layer()) - bridge_host_->layer()->SchedulePaint(rect); + if (ns_window_host_ && ns_window_host_->layer()) + ns_window_host_->layer()->SchedulePaint(rect); } void NativeWidgetMac::ScheduleLayout() { @@ -582,15 +585,15 @@ } void NativeWidgetMac::SetCursor(gfx::NativeCursor cursor) { - if (bridge_impl()) - bridge_impl()->SetCursor(cursor); + if (GetInProcessNSWindowBridge()) + GetInProcessNSWindowBridge()->SetCursor(cursor); } void NativeWidgetMac::ShowEmojiPanel() { // We must plumb the call to ui::ShowEmojiPanel() over the bridge so that it // is called from the correct process. - if (bridge()) - bridge()->ShowEmojiPanel(); + if (GetNSWindowMojo()) + GetNSWindowMojo()->ShowEmojiPanel(); } bool NativeWidgetMac::IsMouseEventsEnabled() const { @@ -608,36 +611,37 @@ // To quote DesktopWindowTreeHostX11, "This method is weird and misnamed." // The goal is to set focus to the content window, thereby removing focus from // any NSView in the window that doesn't belong to toolkit-views. - if (!bridge()) + if (!GetNSWindowMojo()) return; - bridge()->MakeFirstResponder(); + GetNSWindowMojo()->MakeFirstResponder(); } gfx::Rect NativeWidgetMac::GetWorkAreaBoundsInScreen() const { - return bridge_host_ ? bridge_host_->GetCurrentDisplay().work_area() - : gfx::Rect(); + return ns_window_host_ ? ns_window_host_->GetCurrentDisplay().work_area() + : gfx::Rect(); } Widget::MoveLoopResult NativeWidgetMac::RunMoveLoop( const gfx::Vector2d& drag_offset, Widget::MoveLoopSource source, Widget::MoveLoopEscapeBehavior escape_behavior) { - if (!bridge_impl()) + if (!GetInProcessNSWindowBridge()) return Widget::MOVE_LOOP_CANCELED; ReleaseCapture(); - return bridge_impl()->RunMoveLoop(drag_offset) ? Widget::MOVE_LOOP_SUCCESSFUL - : Widget::MOVE_LOOP_CANCELED; + return GetInProcessNSWindowBridge()->RunMoveLoop(drag_offset) + ? Widget::MOVE_LOOP_SUCCESSFUL + : Widget::MOVE_LOOP_CANCELED; } void NativeWidgetMac::EndMoveLoop() { - if (bridge_impl()) - bridge_impl()->EndMoveLoop(); + if (GetInProcessNSWindowBridge()) + GetInProcessNSWindowBridge()->EndMoveLoop(); } void NativeWidgetMac::SetVisibilityChangedAnimationsEnabled(bool value) { - if (bridge()) - bridge()->SetAnimationEnabled(value); + if (GetNSWindowMojo()) + GetNSWindowMojo()->SetAnimationEnabled(value); } void NativeWidgetMac::SetVisibilityAnimationDuration( @@ -663,8 +667,8 @@ transitions = remote_cocoa::mojom::VisibilityTransition::kBoth; break; } - if (bridge()) - bridge()->SetTransitionsToAnimate(transitions); + if (GetNSWindowMojo()) + GetNSWindowMojo()->SetTransitionsToAnimate(transitions); } bool NativeWidgetMac::IsTranslucentWindowOpacitySupported() const { @@ -677,10 +681,10 @@ void NativeWidgetMac::OnSizeConstraintsChanged() { Widget* widget = GetWidget(); - bridge()->SetSizeConstraints(widget->GetMinimumSize(), - widget->GetMaximumSize(), - widget->widget_delegate()->CanResize(), - widget->widget_delegate()->CanMaximize()); + GetNSWindowMojo()->SetSizeConstraints( + widget->GetMinimumSize(), widget->GetMaximumSize(), + widget->widget_delegate()->CanResize(), + widget->widget_delegate()->CanMaximize()); } std::string NativeWidgetMac::GetName() const { @@ -716,12 +720,15 @@ return nullptr; } -remote_cocoa::mojom::NativeWidgetNSWindow* NativeWidgetMac::bridge() const { - return bridge_host_ ? bridge_host_->bridge() : nullptr; +remote_cocoa::mojom::NativeWidgetNSWindow* NativeWidgetMac::GetNSWindowMojo() + const { + return ns_window_host_ ? ns_window_host_->GetNSWindowMojo() : nullptr; } -remote_cocoa::NativeWidgetNSWindowBridge* NativeWidgetMac::bridge_impl() const { - return bridge_host_ ? bridge_host_->bridge_impl() : nullptr; +remote_cocoa::NativeWidgetNSWindowBridge* +NativeWidgetMac::GetInProcessNSWindowBridge() const { + return ns_window_host_ ? ns_window_host_->GetInProcessNSWindowBridge() + : nullptr; } //////////////////////////////////////////////////////////////////////////////// @@ -778,9 +785,9 @@ // static NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow( gfx::NativeWindow window) { - if (NativeWidgetMacNSWindowHost* bridge_host_impl = + if (NativeWidgetMacNSWindowHost* ns_window_host_impl = NativeWidgetMacNSWindowHost::GetFromNativeWindow(window)) { - return bridge_host_impl->native_widget_mac(); + return ns_window_host_impl->native_widget_mac(); } return nullptr; // Not created by NativeWidgetMac. } @@ -788,21 +795,21 @@ // static NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget( gfx::NativeView native_view) { - NativeWidgetMacNSWindowHost* bridge_host = + NativeWidgetMacNSWindowHost* window_host = NativeWidgetMacNSWindowHost::GetFromNativeView(native_view); - if (!bridge_host) + if (!window_host) return nullptr; - while (bridge_host->parent()) - bridge_host = bridge_host->parent(); - return bridge_host->native_widget_mac(); + while (window_host->parent()) + window_host = window_host->parent(); + return window_host->native_widget_mac(); } // static void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view, Widget::Widgets* children) { - NativeWidgetMacNSWindowHost* bridge_host = + NativeWidgetMacNSWindowHost* window_host = NativeWidgetMacNSWindowHost::GetFromNativeView(native_view); - if (!bridge_host) { + if (!window_host) { NSView* ns_view = native_view.GetNativeNSView(); // The NSWindow is not itself a views::Widget, but it may have children that // are. Support returning Widgets that are parented to the NSWindow, except: @@ -826,37 +833,37 @@ // If |native_view| is a subview of the contentView, it will share an // NSWindow, but will itself be a native child of the Widget. That is, adding - // bridge_host->..->GetWidget() to |children| would be adding the _parent_ of + // window_host->..->GetWidget() to |children| would be adding the _parent_ of // |native_view|, not the Widget for |native_view|. |native_view| doesn't have // a corresponding Widget of its own in this case (and so can't have Widget // children of its own on Mac). - if (bridge_host->native_widget_mac()->GetNativeView() != native_view) + if (window_host->native_widget_mac()->GetNativeView() != native_view) return; // Code expects widget for |native_view| to be added to |children|. - if (bridge_host->native_widget_mac()->GetWidget()) - children->insert(bridge_host->native_widget_mac()->GetWidget()); + if (window_host->native_widget_mac()->GetWidget()) + children->insert(window_host->native_widget_mac()->GetWidget()); // When the NSWindow *is* a Widget, only consider children(). I.e. do not - // look through -[NSWindow childWindows] as done for the (!bridge_host) case + // look through -[NSWindow childWindows] as done for the (!window_host) case // above. -childWindows does not support hidden windows, and anything in there // which is not in children() would have been added by AppKit. - for (NativeWidgetMacNSWindowHost* child : bridge_host->children()) + for (NativeWidgetMacNSWindowHost* child : window_host->children()) GetAllChildWidgets(child->native_widget_mac()->GetNativeView(), children); } // static void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view, Widget::Widgets* owned) { - NativeWidgetMacNSWindowHost* bridge_host = + NativeWidgetMacNSWindowHost* window_host = NativeWidgetMacNSWindowHost::GetFromNativeView(native_view); - if (!bridge_host) { + if (!window_host) { GetAllChildWidgets(native_view, owned); return; } - if (bridge_host->native_widget_mac()->GetNativeView() != native_view) + if (window_host->native_widget_mac()->GetNativeView() != native_view) return; - for (NativeWidgetMacNSWindowHost* child : bridge_host->children()) + for (NativeWidgetMacNSWindowHost* child : window_host->children()) GetAllChildWidgets(child->native_widget_mac()->GetNativeView(), owned); } @@ -871,25 +878,25 @@ return; } - NativeWidgetMacNSWindowHost* bridge_host = + NativeWidgetMacNSWindowHost* window_host = NativeWidgetMacNSWindowHost::GetFromNativeView(native_view); - DCHECK(bridge_host); + DCHECK(window_host); gfx::NativeView bridge_view = - bridge_host->native_widget_mac()->GetNativeView(); + window_host->native_widget_mac()->GetNativeView(); gfx::NativeWindow bridge_window = - bridge_host->native_widget_mac()->GetNativeWindow(); + window_host->native_widget_mac()->GetNativeWindow(); bool bridge_is_top_level = - bridge_host->native_widget_mac()->GetWidget()->is_top_level(); + window_host->native_widget_mac()->GetWidget()->is_top_level(); DCHECK([native_view.GetNativeNSView() isDescendantOf:bridge_view.GetNativeNSView()]); DCHECK(bridge_window && ![bridge_window.GetNativeNSWindow() isSheet]); - NativeWidgetMacNSWindowHost* parent_bridge_host = + NativeWidgetMacNSWindowHost* parent_window_host = NativeWidgetMacNSWindowHost::GetFromNativeView(new_parent); // Early out for no-op changes. if (native_view == bridge_view && bridge_is_top_level && - bridge_host->parent() == parent_bridge_host) { + window_host->parent() == parent_window_host) { return; } @@ -903,11 +910,11 @@ // Update |brige_host|'s parent only if // NativeWidgetNSWindowBridge::ReparentNativeView will. if (native_view == bridge_view) { - bridge_host->SetParent(parent_bridge_host); + window_host->SetParent(parent_window_host); if (!bridge_is_top_level) { - // Make |bridge_host|'s NSView be a child of |new_parent| by adding it as + // Make |window_host|'s NSView be a child of |new_parent| by adding it as // a subview. Note that this will have the effect of removing - // |bridge_host|'s NSView from its NSWindow. The |NSWindow| must remain + // |window_host|'s NSView from its NSWindow. The |NSWindow| must remain // visible because it controls the bounds and visibility of the ui::Layer, // so just hide it by setting alpha value to zero. // TODO(ccameron): This path likely violates assumptions. Verify that this
diff --git a/ui/views/widget/native_widget_mac_unittest.mm b/ui/views/widget/native_widget_mac_unittest.mm index 4d77e6a..5974ae32f 100644 --- a/ui/views/widget/native_widget_mac_unittest.mm +++ b/ui/views/widget/native_widget_mac_unittest.mm
@@ -96,8 +96,8 @@ class BridgedNativeWidgetTestApi { public: explicit BridgedNativeWidgetTestApi(NSWindow* window) { - bridge_ = - NativeWidgetMacNSWindowHost::GetFromNativeWindow(window)->bridge_impl(); + bridge_ = NativeWidgetMacNSWindowHost::GetFromNativeWindow(window) + ->GetInProcessNSWindowBridge(); } // Simulate a frame swap from the compositor. @@ -1620,14 +1620,14 @@ NSWindow* parent = MakeBorderlessNativeParent(); Widget* dialog = views::DialogDelegate::CreateDialogWidget( new DialogDelegateView, nullptr, [parent contentView]); - NativeWidgetMacNSWindowHost* bridge_host = + NativeWidgetMacNSWindowHost* window_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow( dialog->GetNativeWindow()); - EXPECT_EQ(bridge_host->parent()->native_widget_mac()->GetNativeWindow(), + EXPECT_EQ(window_host->parent()->native_widget_mac()->GetNativeWindow(), parent); Widget::ReparentNativeView(dialog->GetNativeView(), [parent contentView]); - EXPECT_EQ(bridge_host->parent()->native_widget_mac()->GetNativeWindow(), + EXPECT_EQ(window_host->parent()->native_widget_mac()->GetNativeWindow(), parent); [parent close]; @@ -1636,13 +1636,13 @@ parent = parent_widget->GetNativeWindow().GetNativeNSWindow(); dialog = views::DialogDelegate::CreateDialogWidget( new DialogDelegateView, nullptr, [parent contentView]); - bridge_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow( + window_host = NativeWidgetMacNSWindowHost::GetFromNativeWindow( dialog->GetNativeWindow()); - EXPECT_EQ(bridge_host->parent()->native_widget_mac()->GetNativeWindow(), + EXPECT_EQ(window_host->parent()->native_widget_mac()->GetNativeWindow(), parent); Widget::ReparentNativeView(dialog->GetNativeView(), [parent contentView]); - EXPECT_EQ(bridge_host->parent()->native_widget_mac()->GetNativeWindow(), + EXPECT_EQ(window_host->parent()->native_widget_mac()->GetNativeWindow(), parent); parent_widget->CloseNow(); @@ -2204,7 +2204,7 @@ widget_ = CreateTopLevelPlatformWidget(); bridge_ = NativeWidgetMacNSWindowHost::GetFromNativeWindow( widget_->GetNativeWindow()) - ->bridge_impl(); + ->GetInProcessNSWindowBridge(); fake_full_keyboard_access_ = ui::test::ScopedFakeFullKeyboardAccess::GetInstance(); DCHECK(fake_full_keyboard_access_);
diff --git a/ui/webui/resources/polymer_resources_v3.grdp b/ui/webui/resources/polymer_resources_v3.grdp index da855bd..db114b80 100644 --- a/ui/webui/resources/polymer_resources_v3.grdp +++ b/ui/webui/resources/polymer_resources_v3.grdp
@@ -312,310 +312,9 @@ file="../../../third_party/polymer/v3_0/components-chromium/paper-tooltip/paper-tooltip.js" type="chrome_html" compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_EXTERNS_CLOSURE_TYPES_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/externs/closure-types.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_EXTERNS_POLYMER_DOM_API_EXTERNS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-dom-api-externs.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_EXTERNS_POLYMER_EXTERNS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-externs.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_EXTERNS_POLYMER_ICONSET_EXTERNS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-iconset-externs.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_EXTERNS_POLYMER_INTERNAL_SHARED_TYPES_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/externs/polymer-internal-shared-types.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_EXTERNS_WEBCOMPONENTS_EXTERNS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/externs/webcomponents-externs.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_ELEMENTS_ARRAY_SELECTOR_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/elements/array-selector.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_ELEMENTS_CUSTOM_STYLE_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/elements/custom-style.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_ELEMENTS_DOM_BIND_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-bind.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_ELEMENTS_DOM_IF_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-if.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_ELEMENTS_DOM_MODULE_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-module.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_ELEMENTS_DOM_REPEAT_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/elements/dom-repeat.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_CLASS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/class.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_LEGACY_DATA_MIXIN_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-data-mixin.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_LEGACY_ELEMENT_MIXIN_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/legacy-element-mixin.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_MUTABLE_DATA_BEHAVIOR_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/mutable-data-behavior.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_POLYMER_DOM_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer.dom.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_POLYMER_FN_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/polymer-fn.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_LEGACY_TEMPLATIZER_BEHAVIOR_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/legacy/templatizer-behavior.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_DIR_MIXIN_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/dir-mixin.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_DISABLE_UPGRADE_MIXIN_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/disable-upgrade-mixin.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_ELEMENT_MIXIN_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/element-mixin.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_GESTURE_EVENT_LISTENERS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/gesture-event-listeners.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_MUTABLE_DATA_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/mutable-data.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_PROPERTIES_CHANGED_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-changed.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_PROPERTIES_MIXIN_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/properties-mixin.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_PROPERTY_ACCESSORS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-accessors.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_PROPERTY_EFFECTS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/property-effects.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_STRICT_BINDING_PARSER_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/strict-binding-parser.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_MIXINS_TEMPLATE_STAMP_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/mixins/template-stamp.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_ARRAY_SPLICE_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/array-splice.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_ASYNC_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/async.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_BOOT_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/boot.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_CASE_MAP_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/case-map.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_DEBOUNCE_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/debounce.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_FLATTENED_NODES_OBSERVER_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flattened-nodes-observer.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_FLUSH_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/flush.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_GESTURES_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/gestures.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_HTML_TAG_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/html-tag.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_MIXIN_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/mixin.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_PATH_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/path.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_RENDER_STATUS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/render-status.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_RESOLVE_URL_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/resolve-url.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_SETTINGS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/settings.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_STYLE_GATHER_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/style-gather.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_TELEMETRY_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/telemetry.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_TEMPLATIZE_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/templatize.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_UNRESOLVED_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/unresolved.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_LIB_UTILS_WRAP_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/lib/utils/wrap.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_POLYMER_ELEMENT_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/polymer-element.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_POLYMER_POLYMER_LEGACY_JS" - file="../../../third_party/polymer/v3_0/components-chromium/polymer/polymer-legacy.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_APPLY_SHIM_MIN_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/apply-shim.min.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_CUSTOM_STYLE_INTERFACE_MIN_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/custom-style-interface.min.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_ENTRYPOINTS_APPLY_SHIM_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/apply-shim.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_ENTRYPOINTS_CUSTOM_STYLE_INTERFACE_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/custom-style-interface.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_ENTRYPOINTS_SCOPING_SHIM_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/entrypoints/scoping-shim.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_EXTERNS_SHADYCSS_EXTERNS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/externs/shadycss-externs.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SCOPING_SHIM_MIN_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/scoping-shim.min.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_APPLY_SHIM_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_APPLY_SHIM_UTILS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/apply-shim-utils.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_COMMON_REGEX_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/common-regex.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_COMMON_UTILS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/common-utils.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_CSS_PARSE_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/css-parse.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_CUSTOM_STYLE_INTERFACE_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/custom-style-interface.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_DOCUMENT_WAIT_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/document-wait.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_DOCUMENT_WATCHER_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/document-watcher.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_SCOPING_SHIM_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/scoping-shim.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_CACHE_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-cache.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_INFO_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-info.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_PLACEHOLDER_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-placeholder.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_PROPERTIES_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-properties.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_SETTINGS_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-settings.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_TRANSFORMER_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-transformer.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_STYLE_UTIL_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/style-util.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_TEMPLATE_MAP_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/template-map.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_3_0_SHADYCSS_SRC_UNSCOPED_STYLE_HANDLER_JS" - file="../../../third_party/polymer/v3_0/components-chromium/shadycss/src/unscoped-style-handler.js" + <structure name="IDR_POLYMER_3_0_POLYMER_POLYMER_BUNDLED_MIN_JS" + file="../../../third_party/polymer/v3_0/components-chromium/polymer/polymer_bundled.min.js" type="chrome_html" compress="gzip" /> - </grit-part>
diff --git a/ui/wm/core/OWNERS b/ui/wm/core/OWNERS index 37a23e8..5c9931ca 100644 --- a/ui/wm/core/OWNERS +++ b/ui/wm/core/OWNERS
@@ -1,2 +1 @@ -per-file shadow*=derat@chromium.org per-file shadow*=estade@chromium.org